问题
每当有关Java同步的问题出现时,有些人非常渴望指出应该避免使用synchronized(this)
。相反,他们声称,首选锁定私人参考。
一些给出的原因是:
- 一些邪恶的代码可能会偷你的锁(这个非常受欢迎,也有一个"意外"的变种)
- 同一类中的所有同步方法使用完全相同的锁,这会降低吞吐量
- 你(不必要地)暴露了太多信息
其他人,包括我在内,认为synchronized(this)
是一个习惯用法(也在Java库中使用很多),是安全且易于理解的。不应该避免它,因为你有一个错误,你不知道多线程程序中发生了什么。换句话说:如果适用,则使用它。
我有兴趣看到一些现实世界的例子(没有foobar的东西)避免锁定onthis
是最好的,当synchronized(this)
也可以完成这项工作。
因此:你是否应始终避免使用synchronized(this)
并将其替换为私有引用上的锁定?
一些进一步的信息(更新为答案):
- 我们正在谈论实例同步
- 考虑隐式(同步方法)和同步(this)的显式形式
- 如果你引用Bloch或其他有关该主题的权限,请不要遗漏你不喜欢的部分(例如,有效Java,线程安全项目:通常它是实例本身的锁,但也有例外。)
- 如果你需要锁定粒度而不是synchronized(this)提供的,那么synchronized(this)不适用,所以这不是问题
#1 热门回答(112 赞)
我将分别介绍每一点。
- 一些邪恶的代码可能会偷你的锁(这个非常受欢迎,也有一个"意外"的变种)我更加担心意外。它相当于使用它是你的类暴露界面的一部分,应该记录下来。有时需要其他代码使用锁的能力。像Collections.synchronizedMap这样的东西也是如此(参见javadoc)。
- 同一类中的所有同步方法使用完全相同的锁,这会降低吞吐量。这种思维过于简单化;只是摆脱synchronized(this)将无法解决问题。适当的吞吐量同步需要更多考虑。
- 你(不必要地)暴露太多信息这是#1的变种。使用synchronized(this)是界面的一部分。如果你不想/需要暴露,请不要这样做。
#2 热门回答(81 赞)
那么,首先应该指出:
public void blah() {
synchronized (this) {
// do stuff
}
}
在语义上等同于:
public synchronized void blah() {
// do stuff
}
这是不使用synchronized(this)
的一个原因。你可能会争辩说你可以在synchronized(this)
块左右做些什么。通常的原因是尝试避免必须进行同步检查,这会导致各种并发问题,特别是double checked-locking problem,它只是表明制作相对简单的检查线程安全是多么困难。
私人锁定是一种防御机制,这绝不是一个坏主意。
此外,正如你所提到的,私有锁可以控制粒度。对象上的一组操作可能与另一组操作完全无关,但是synchronized(this)
将相互排除对所有操作的访问。
synchronized(this)
我真的不给你任何东西。
#3 热门回答(49 赞)
在使用synchronized(this)时,你正在使用类实例作为锁本身。这意味着当线程1获取锁定时,线程2应该等待
假设以下代码
public void method1() {
do something ...
synchronized(this) {
a ++;
}
................
}
public void method2() {
do something ...
synchronized(this) {
b ++;
}
................
}
方法1修改变量和方法2修改变量b,应该避免两个线程同时修改同一个变量。但是,whilethread1modifyingaandthread2modifyingbit可以在没有任何竞争条件的情况下执行。
不幸的是,上面的代码不允许这样做,因为我们对锁使用相同的引用;这意味着线程即使它们不处于竞争状态也应该等待,显然代码牺牲了程序的并发性。
解决方案是使用2个不同的锁来处理不同的变量。
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个锁而不是一个(lockAandlockBfor variablesaandbrespectively),结果允许更好的并发性,另一方面它变得比第一个例子更复杂......