同步方法中的同步块

The*_*ger 5 java concurrency multithreading synchronization

我正在查看包含synchronized方法的第三方库中的一些代码,并且在此方法中有一个锁定实例变量的synchronized块.它与此类似:

public class Foo {
   final Bar bar = new Bar();

   public synchronized void doSomething() {
       // do something
       synchronized(bar) {
           // update bar
       }
   }
   ...
}
Run Code Online (Sandbox Code Playgroud)

这有意义吗?如果是这样,在同步方法中有一个同步语句有什么好处?

鉴于同步方法锁定整个对象,对我来说似乎是多余的.在处理非私有的实例变量时,这种方法是否有意义?

And*_*own 8

在您的示例中,该方法锁定Foo对象的实例,锁定对象的实例bar.其他方法可能只是锁定对象的实例Foo 对象的实例bar.

所以,是的,这取决于他们究竟在做什么.据推测,bar可以保护一些较小的数据子集,而某些方法只需要锁定bar以线程安全的方式执行其操作.

是什么synchronized

synchronized(在方法上或在语句中)创建一个互斥区域(关键部分,或者,特别是对于Java,一个可重入的互斥锁).线程进入临界区的"键"是synchronized语句‡中使用的对象引用.只有一个线程可以(递归地)在使用相同密钥的所有块上"拥有"该"密钥"; 也就是说,一次只有一个线程可以synchronized在给定的对象引用上输入任何块.

这样一个关键部分简单地阻止了您在块内执行的操作(变量读/写)与锁定同一对象引用的所有其他关键部分中的任何其他操作同时发生.(它不会自动保护对象内的所有变量).

在Java中,这样一个关键部分也会创建一个先发生过的合同.

以身作则

作为一个有点人为的例子:

public class Foo {
    final Bar bar = new Bar();

    private int instanceCounter = 0;
    private int barCounter = 0;

    public synchronized void incrementBarCounterIfAllowed() {
        synchronized (bar) {
            if (instanceCounter < 10) barCounter++;
        }
    }

    public synchronized void incrementClassCounter() {
        instanceCounter++;
    }

    public void incrementBarCounter() {
        synchronized (bar) {
            barCounter++;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

实例变量是否是私有的并不重要,这种方法是否适用.在单个类中,您可以拥有多个锁定对象,每个锁定对象都保护自己的数据集.

但是,执行此操作的风险是您必须非常严格地使用编码约定来通过在不同位置锁定不同顺序的两个锁来防止死锁.例如,如果您从代码中的其他位置执行此操作,请使用上面的代码:

synchronized(myFoo.bar) {
  myFoo.incrementClassCounter();
}
Run Code Online (Sandbox Code Playgroud)

你冒这个incrementBarCounterIfAllowed()方法的死锁风险

请注意,这barCounter可能是Bar等等的实例变量- 为了简洁起见,我避免使用代码示例.

对于synchronized方法,该引用是对类实例的引用(或Class<?>对于static方法的类).