如果我在同一个类中有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()吗?


当前回答

被访问的锁在对象上,而不是方法上。在方法中访问哪些变量无关紧要。

在方法中添加"synchronized"意味着运行代码的线程必须在继续之前获得对象上的锁。添加“static synchronized”意味着运行代码的线程必须在继续之前获取类对象上的锁。或者,你也可以像这样将代码封装在一个块中:

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

这样您就可以指定必须获取其锁的对象。

如果你想避免锁定包含对象,你可以选择:

使用指定不同锁的同步块 使a和b具有原子性(使用java.util.concurrent.atomic)

其他回答

被访问的锁在对象上,而不是方法上。在方法中访问哪些变量无关紧要。

在方法中添加"synchronized"意味着运行代码的线程必须在继续之前获得对象上的锁。添加“static synchronized”意味着运行代码的线程必须在继续之前获取类对象上的锁。或者,你也可以像这样将代码封装在一个块中:

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

这样您就可以指定必须获取其锁的对象。

如果你想避免锁定包含对象,你可以选择:

使用指定不同锁的同步块 使a和b具有原子性(使用java.util.concurrent.atomic)

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

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

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

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

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

这个例子(虽然不是很漂亮)可以提供更多关于锁定机制的见解。如果incrementA是同步的,而incrementB没有同步,那么incrementB将尽快执行,但如果incrementB也是同步的,那么它必须“等待”incrementA完成,然后incrementB才能完成它的工作。

这两个方法都被调用到单个实例对象上,在这个例子中是:job,而“竞争”线程是aThread和main。

尝试在incrementB中使用'synchronized'和不使用它,你会看到不同的结果。如果incrementB也是“同步”的,那么它必须等待incrementA()完成。每个变体运行几次。

class LockTest implements Runnable {
    int a = 0;
    int b = 0;

    public synchronized void incrementA() {
        for (int i = 0; i < 100; i++) {
            this.a++;
            System.out.println("Thread: " + Thread.currentThread().getName() + "; a: " + this.a);
        }
    }

    // Try with 'synchronized' and without it and you will see different results
    // if incrementB is 'synchronized' as well then it has to wait for incrementA() to finish

    // public void incrementB() {
    public synchronized void incrementB() {
        this.b++;
        System.out.println("*************** incrementB ********************");
        System.out.println("Thread: " + Thread.currentThread().getName() + "; b: " + this.b);
        System.out.println("*************** incrementB ********************");
    }

    @Override
    public void run() {
        incrementA();
        System.out.println("************ incrementA completed *************");
    }
}

class LockTestMain {
    public static void main(String[] args) throws InterruptedException {
        LockTest job = new LockTest();
        Thread aThread = new Thread(job);
        aThread.setName("aThread");
        aThread.start();
        Thread.sleep(1);
        System.out.println("*************** 'main' calling metod: incrementB **********************");
        job.incrementB();
    }
}

你可以像下面这样做。在这种情况下,您使用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方法应用于指向....的WHOLE类对象但无论如何,它只会在addA或addB方法中执行求和时阻塞其他线程的执行,因为当它完成…一个线程将释放对象,另一个线程将访问另一个方法,以此类推。

我的意思是“同步”是为了在特定的代码执行中阻止另一个线程访问另一个线程而精确制作的。最后这段代码就可以正常工作了。

作为最后的注意,如果有一个'a'和'b'变量,而不仅仅是一个唯一的变量'a'或任何其他名称,没有必要同步这个方法,因为它是完全安全的访问其他var(其他内存位置)。

class X {

private int a;
private int b;

public void addA(){
    a++;
}

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

也会起作用