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


当前回答

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

其他回答

这个例子(虽然不是很漂亮)可以提供更多关于锁定机制的见解。如果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();
    }
}

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

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

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

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

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

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

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

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

从oracle文档链接

使方法同步有两个效果:

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object. Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads

看看这个文档页,了解内在锁和锁的行为。

这将回答您的问题:在同一个对象x上,当其中一个同步方法正在执行时,您不能同时调用x.a da()和x.a dbb()。