每当在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


当前回答

这里已经说过,同步块可以使用用户定义的变量作为锁对象,当同步函数只使用“this”时。当然,你也可以对函数中需要同步的部分进行操作。

但是每个人都说synchronized函数和block之间没有区别,block覆盖了使用“this”作为锁对象的整个函数。这是不对的,不同的是字节码,将在这两种情况下产生。在同步块使用的情况下,应该分配本地变量,其中包含引用“this”。因此,我们会得到一个更大的函数(如果你只有几个函数,这就无关紧要了)。

你可以在这里找到更详细的解释: http://www.artima.com/insidejvm/ed2/threadsynchP.html

同步块的使用也不好,原因如下:

synchronized关键字在一个方面非常有限:当退出一个同步块时,所有等待该锁的线程都必须被解除阻塞,但只有其中一个线程可以获得锁;所有其他人都看到锁已被占用,并返回阻塞状态。这不仅仅是浪费了大量的处理周期:为解除线程阻塞而进行的上下文切换通常还涉及从磁盘调出内存,这是非常非常昂贵的。

关于这方面的更多细节,我建议你阅读这篇文章: http://java.dzone.com/articles/synchronized-considered

其他回答

这里已经说过,同步块可以使用用户定义的变量作为锁对象,当同步函数只使用“this”时。当然,你也可以对函数中需要同步的部分进行操作。

但是每个人都说synchronized函数和block之间没有区别,block覆盖了使用“this”作为锁对象的整个函数。这是不对的,不同的是字节码,将在这两种情况下产生。在同步块使用的情况下,应该分配本地变量,其中包含引用“this”。因此,我们会得到一个更大的函数(如果你只有几个函数,这就无关紧要了)。

你可以在这里找到更详细的解释: http://www.artima.com/insidejvm/ed2/threadsynchP.html

同步块的使用也不好,原因如下:

synchronized关键字在一个方面非常有限:当退出一个同步块时,所有等待该锁的线程都必须被解除阻塞,但只有其中一个线程可以获得锁;所有其他人都看到锁已被占用,并返回阻塞状态。这不仅仅是浪费了大量的处理周期:为解除线程阻塞而进行的上下文切换通常还涉及从磁盘调出内存,这是非常非常昂贵的。

关于这方面的更多细节,我建议你阅读这篇文章: http://java.dzone.com/articles/synchronized-considered

我认为第一点(其他人使用您的锁)和第二点(所有方法不必要地使用相同的锁)可能发生在任何相当大的应用程序中。特别是当开发人员之间没有良好的沟通时。

这不是一成不变的,这主要是一个良好的实践和防止错误的问题。

我将分别讨论每一点。

Some evil code may steal your lock (very popular this one, also has an "accidentally" variant) I'm more worried about accidentally. What it amounts to is that this use of this is part of your class' exposed interface, and should be documented. Sometimes the ability of other code to use your lock is desired. This is true of things like Collections.synchronizedMap (see the javadoc). All synchronized methods within the same class use the exact same lock, which reduces throughput This is overly simplistic thinking; just getting rid of synchronized(this) won't solve the problem. Proper synchronization for throughput will take more thought. You are (unnecessarily) exposing too much information This is a variant of #1. Use of synchronized(this) is part of your interface. If you don't want/need this exposed, don't do it.

一个使用synchronized(this)的好例子。

// add listener
public final synchronized void addListener(IListener l) {listeners.add(l);}
// remove listener
public final synchronized void removeListener(IListener l) {listeners.remove(l);}
// routine that raise events
public void run() {
   // some code here...
   Set ls;
   synchronized(this) {
      ls = listeners.clone();
   }
   for (IListener l : ls) { l.processEvent(event); }
   // some code here...
}

正如你在这里看到的,我们在这个上使用同步来方便地与那里的一些同步方法进行长周期(可能是无限循环的run方法)合作。

当然,在私有字段上使用synchronized可以很容易地重写。但有时,当我们已经有了一些同步方法的设计时(例如,我们从遗留类派生出来的,synchronized(this)可能是唯一的解决方案)。

concurrent包极大地降低了线程安全代码的复杂性。我只有一些轶事证据,但我所见过的大多数synchronized(x)工作似乎都是重新实现Lock、Semaphore或Latch,但使用的是较低级别的监视器。

考虑到这一点,使用这些机制中的任何一种进行同步都类似于对内部对象进行同步,而不是泄露锁。这是非常有益的,因为您可以绝对确定通过两个或多个线程控制进入监视器的条目。