每当在SO上出现关于Java同步的问题时,有些人都急于指出应该避免同步(这一点)。相反,他们声称,对私有引用的锁定是首选的。

其中一些原因是:

一些邪恶的代码可能会偷你的锁(这个很流行,也有一个“意外”变体) 同一类中的所有同步方法都使用完全相同的锁,这降低了吞吐量 你(不必要地)暴露了太多信息

包括我在内的其他人认为,synchronized(this)是一个被大量使用的习语(在Java库中也是如此),是安全的,而且很容易理解。它不应该被避免,因为你有一个错误,你不知道你的多线程程序中发生了什么。换句话说:如果它是适用的,那么就使用它。

我感兴趣的是看到一些现实世界的例子(没有foobar的东西),避免锁定在这是可取的,当同步(这)也会做的工作。

因此:您应该总是避免synchronized(this),并将其替换为私有引用上的锁吗?


一些进一步的信息(随着答案的更新):

we are talking about instance synchronization both implicit (synchronized methods) and explicit form of synchronized(this) are considered if you quote Bloch or other authorities on the subject, don't leave out the parts you don't like (e.g. Effective Java, item on Thread Safety: Typically it is the lock on the instance itself, but there are exceptions.) if you need granularity in your locking other than synchronized(this) provides, then synchronized(this) is not applicable so that's not the issue


当前回答

简单的回答:您必须了解其中的区别,并根据代码做出选择。

Long answer: In general I would rather try to avoid synchronize(this) to reduce contention but private locks add complexity you have to be aware of. So use the right synchronization for the right job. If you are not so experienced with multi-threaded programming I would rather stick to instance locking and read up on this topic. (That said: just using synchronize(this) does not automatically make your class fully thread-safe.) This is a not an easy topic but once you get used to it, the answer whether to use synchronize(this) or not comes naturally.

其他回答

首先需要指出的是:

public void blah() {
  synchronized (this) {
    // do stuff
  }
}

语义上等价于:

public synchronized void blah() {
  // do stuff
}

这是不使用synchronized(this)的一个原因。您可能会说,您可以围绕synchronized(this)块做一些事情。通常的原因是试图避免必须进行同步检查,这将导致各种并发问题,特别是双重检查锁定问题,这表明要使一个相对简单的检查具有线程安全性是多么困难。

私有锁是一种防御机制,这从来都不是一个坏主意。

另外,正如您所提到的,私有锁可以控制粒度。一个对象上的一组操作可能与另一组完全无关,但同步(这)将相互排除对所有这些操作的访问。

同步(这个)真的不能给你任何东西。

简单的回答:您必须了解其中的区别,并根据代码做出选择。

Long answer: In general I would rather try to avoid synchronize(this) to reduce contention but private locks add complexity you have to be aware of. So use the right synchronization for the right job. If you are not so experienced with multi-threaded programming I would rather stick to instance locking and read up on this topic. (That said: just using synchronize(this) does not automatically make your class fully thread-safe.) This is a not an easy topic but once you get used to it, the answer whether to use synchronize(this) or not comes naturally.

避免使用synchronized(this)作为锁定机制:这会锁定整个类实例,并可能导致死锁。在这种情况下,重构代码以只锁定特定的方法或变量,这样整个类就不会被锁定。同步可以在方法级别内使用。 下面的代码展示了如何锁定一个方法,而不是使用synchronized(this)。

   public void foo() {
if(operation = null) {
    synchronized(foo) { 
if (operation == null) {
 // enter your code that this method has to handle...
          }
        }
      }
    }

这实际上只是对其他答案的补充,但如果你对使用私有对象进行锁定的主要反对意见是,它会使你的类与与业务逻辑无关的字段混乱,那么Project Lombok有@Synchronized在编译时生成样板:

@Synchronized
public int foo() {
    return 0;
}

编译,

private final Object $lock = new Object[0];

public int foo() {
    synchronized($lock) {
        return 0;
    }
}

当您使用synchronized(this)时,您正在使用类实例作为锁本身。这意味着当线程1获得锁时,线程2应该等待。

假设有以下代码:

public void method1() {
    // do something ...
    synchronized(this) {
        a ++;      
    }
    // ................
}


public void method2() {
    // do something ...
    synchronized(this) {
        b ++;      
    }
    // ................
}

方法一修改变量a,方法二修改变量b,应避免两个线程同时修改同一个变量。但是当thread1修改a和thread2修改b时,它可以在没有任何竞争条件的情况下执行。

不幸的是,上面的代码不允许这样做,因为我们对锁使用了相同的引用;这意味着线程即使没有处于竞争状态也应该等待,显然代码牺牲了程序的并发性。

解决方案是对两个不同的变量使用两个不同的锁:

public class Test {

    private Object lockA = new Object();
    private Object lockB = new Object();

    public void method1() {
        // do something ...
        synchronized(lockA) {
            a ++;      
        }
        // ................
    }


    public void method2() {
        // do something ...
        synchronized(lockB) {
            b ++;      
        }
        // ................
    }

}

上面的例子使用了更细粒度的锁(2个锁而不是一个锁(分别针对变量a和变量b的lockA和lockB),结果允许更好的并发性,另一方面,它变得比第一个例子更复杂……