考虑以下:
- Let Thread1
run() { alphonse.bow(gaston); }
- Let Thread2
run() { gaston.bow(alphonse); }
-
Thread1 enters
alphonse.bow(gaston);
, 锁定alphonse
since bow()
is synchronized
-
Thread2 enters
gaston.bow(alphonse);
, 锁定gaston
since bow()
is synchronized
- In Thread1,
bower.bowBack(this);
evaluates to gaston.bowBack(alphonse);
-
Thread1尝试获取锁
gaston
,目前由Thread2
- In Thread2,
bower.bowBack(this);
evaluates to alphonse.bowBack(gaston);
-
Thread2尝试获取锁
alphonse
,目前由Thread1
- 每个线程都在等待另一个线程释放锁,因此出现死锁
问题是存在过多synchronized
现在。有很多方法可以“解决”这个问题;这是一个有指导意义的解决方案:
public void bow(Friend bower) {
synchronized (this) {
System.out.format("%s: %s has bowed to me!%n",
this.name, bower.getName());
}
bower.bowBack(this);
}
public synchronized void bowBack(Friend bower) {
System.out.format("%s: %s has bowed back to me!%n",
this.name, bower.getName());
}
Now bowBack()
是完全synchronized
, but bow()
只是synchronized
部分地,使用synchronized(this)
陈述。这将防止僵局。
以下是来自的引述《Effective Java》第二版,第 67 条:避免过度同步
为了避免活性和安全故障,绝不将控制权交给客户在一个synchronized
方法或块。换句话说,在一个synchronized
区域,不要调用设计为被重写的方法,或由客户端以函数对象的形式提供的方法。从class
与synchronized
区域,此类方法是alien。该类不知道该方法的作用,也无法控制它。根据外来方法的作用,从synchronized
区域可能会导致异常、死锁或数据损坏。
[...] 作为一项规则,您应该在内部做尽可能少的工作synchronized
地区。获取锁,检查共享数据,必要时对其进行转换,然后删除锁。
在本质上,bower.bowBack(this)
是试图将控制权让给alien方法,因为bowBack()
不是一个final
中的方法class Friend
。例如,考虑以下尝试来解决该问题:
// attempt to fix: STILL BROKEN!!!
public synchronized void bow(Friend bower) {
System.out.format("%s: %s has bowed to me!%n",
this.name, bower.getName());
bower.bowBack(this);
// ceding control to alien method within synchronized block!
}
// not a final method, subclasses may @Override
public void bowBack(Friend bower) {
System.out.format("%s: %s has bowed back to me!%n",
this.name, bower.getName());
}
上面的代码不会与当前的发生死锁alphonse/gaston
场景,但自从bow()
将控制权让给非final
method bowBack()
, 子类可以@Override
该方法会导致bow()
陷入僵局。那是,bowBack()
is an alien方法bow()
,因此应该NOT已从内部调用synchronized
region.
参考
- JLS 8.4.3.6synchronized Methods http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.3.6
- JLS 14.19synchronized陈述 http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.19
- JLS 17.1 锁 http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.1
See also
-
Effective Java 2nd Edition
- 第 66 项:同步对共享可变数据的访问
- 第 15 项:最小化可变性