任何人都可以解释为什么这段代码中存在死锁。谢谢
public class Deadlock {
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public synchronized void bow(Friend bower) {
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());
}
}
public static void main(String[] args) {
final Friend alphonse = new Friend("Alphonse");
final Friend gaston = new Friend("Gaston");
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}).start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}).start();
}
}
答案 0 :(得分:25)
请考虑以下事项:
run() { alphonse.bow(gaston); }
run() { gaston.bow(alphonse); }
alphonse.bow(gaston);
,锁定alphonse
,因为bow()
为synchronized
gaston.bow(alphonse);
,锁定gaston
,因为bow()
为synchronized
bower.bowBack(this);
评估为gaston.bowBack(alphonse);
gaston
的锁定
bower.bowBack(this);
评估为alphonse.bowBack(gaston);
alphonse
的锁定
问题是目前有过多的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());
}
现在bowBack()
已完全synchronized
,但bow()
仅部分synchronized
,使用synchronized(this)
语句。这样可以防止死锁。
以下是 Effective Java 2nd Edition的引用,第67项:避免过度同步
为了避免活跃和安全失败,永远不会在
synchronized
方法或块中放弃对客户端的控制。换句话说,在synchronized
区域内,不要调用设计为被覆盖的方法,或者由客户端以函数对象的形式提供的方法。从class
与synchronized
区域的角度来看,此类方法是 alien 。该类不知道该方法的作用,也无法控制它。根据外来方法的作用,从synchronized
区域调用它可能会导致异常,死锁或数据损坏。[...]通常,您应该在
synchronized
区域内尽可能少地完成工作。获取锁,检查共享数据,必要时进行转换,然后放下锁。
实质上,bower.bowBack(this)
试图将控制权交给外来方法,因为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
方法bowBack()
,因此子类可以{{1这种方法会导致@Override
死锁。也就是说,bow()
是bowBack()
的异类方法,因此应该从bow()
区域内调用 NOT 。
答案 1 :(得分:13)
以下是它可能会被执行的方式。
alphonse.bow(gaston);
,由于synchronized
关键字gaston.bow(alphonse);
,gaston现已锁定bower.bowBack(this);
方法调用执行bow
,因为gaston(bower)已被锁定。等待锁被释放。bower.bowBack(this);
方法调用执行bow
,因为alphonse(bower)已被锁定。等待锁被释放。两个线程都互相等待释放锁定。
答案 2 :(得分:1)
最好的理解方法是在调用bower.bowBack
之前将其放在bow()中的代码下面try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}