如果我在同一个类中有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++;
      }
   }
}

其他回答

你可以像下面这样做。在这种情况下,您使用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 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()。

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

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

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

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

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

这可能行不通,因为从Integer到int的装箱和自动装箱依赖于JVM,如果两个不同的数字在-128和127之间,很可能会被散列到相同的地址。