如果我在同一个类中有2个同步方法,但是每个方法访问不同的变量,2个线程可以同时访问这2个方法吗?锁是发生在对象上,还是与同步方法中的变量一样特定?

例子:

class X {

    private int a;
    private int b;

    public synchronized void addA(){
        a++;
    }

    public synchronized void addB(){
        b++;
    }

}

两个线程可以同时访问类X的同一个实例,执行x.a da()和x.a bbb()吗?


当前回答

你可以像下面这样做。在这种情况下,您使用a和b上的锁来同步,而不是“this”上的锁。我们不能使用int,因为原始值没有锁,所以我们使用Integer。

class x{
   private Integer a;
   private Integer b;
   public void addA(){
      synchronized(a) {
         a++;
      }
   }
   public synchronized void addB(){
      synchronized(b) {
         b++;
      }
   }
}

其他回答

Synchronized在方法声明上是语法糖:

 public void addA() {
     synchronized (this) {
          a++;
     }
  }

在静态方法中,它是这样的语法糖:

 ClassA {
     public static void addA() {
          synchronized(ClassA.class) {
              a++;
          }
 }

我认为,如果Java设计人员当时就知道现在对同步的理解,他们就不会添加语法糖,因为它通常会导致糟糕的并发实现。

如果你将方法声明为synchronized(就像你通过输入public synchronized void addA()所做的那样),你就同步了整个对象,所以两个线程从同一个对象访问不同的变量,无论如何都会阻塞彼此。

如果您希望一次只同步一个变量,以便两个线程在访问不同变量时不会相互阻塞,则可以在synchronized()块中分别对它们进行同步。如果a和b是对象引用,你将使用:

public void addA() {
    synchronized( a ) {
        a++;
    }
}

public void addB() {
    synchronized( b ) {
        b++;
    }
}

但因为它们是原始的,你不能这样做。

我建议你改用AtomicInteger:

import java.util.concurrent.atomic.AtomicInteger;

class X {

    AtomicInteger a;
    AtomicInteger b;

    public void addA(){
        a.incrementAndGet();
    }

    public void addB(){ 
        b.incrementAndGet();
    }
}

在java同步中,如果一个线程想要进入同步方法,它将获得该对象的所有同步方法的锁,而不仅仅是线程正在使用的一个同步方法。 因此,执行addA()的线程将获得对addA()和addB()的锁,因为两者都是同步的。因此具有相同对象的其他线程不能执行addB()。

如果你有一些方法没有同步,并且正在访问和改变实例变量。在你的例子中:

 private int a;
 private int b;

当其他线程处于同一对象的同步方法中时,任何数量的线程都可以同时访问这些非同步方法,并可以对实例变量进行更改。 例如:

 public void changeState() {
      a++;
      b++;
    }

您需要避免出现非同步方法访问实例变量并对其进行更改的情况,否则就没有使用同步方法的意义。

在以下场景中:-

class X {

        private int a;
        private int b;

        public synchronized void addA(){
            a++;
        }

        public synchronized void addB(){
            b++;
        }
     public void changeState() {
          a++;
          b++;
        }
    }

只有一个线程可以在addA或addB方法,但同时任何数量的线程都可以进入changeState方法。没有两个线程可以同时进入addA和addB(因为对象级锁定),但同时任何数量的线程都可以进入changeState。

你可以像下面这样做。在这种情况下,您使用a和b上的锁来同步,而不是“this”上的锁。我们不能使用int,因为原始值没有锁,所以我们使用Integer。

class x{
   private Integer a;
   private Integer b;
   public void addA(){
      synchronized(a) {
         a++;
      }
   }
   public synchronized void addB(){
      synchronized(b) {
         b++;
      }
   }
}