有人可以解释为什么以下代码导致死锁。
我的理解是,当alphonse(线程)运行然后它获取对朋友obj的锁定,因为它调用bow()方法但是gaston(另一个线程)如何能够获得同一朋友obj上的锁定而alphonse还没有完成/释放朋友obj的锁定。
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.println("invoked by " + name);
System.out.format("%s: %s"
+ " has bowed to me!%n",
this.name, bower.getName());
bower.bowBack(this);
System.out.printf("finished by " + name);
}
public synchronized void bowBack(Friend bower) {
System.out.format("%s: %s"
+ " has bowed back to me!%n",
this.name, bower.getName());
System.out.println("exiting bowBack()");
}
}
public static void main(String[] args) throws InterruptedException {
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 :(得分:6)
alphonse.bow()
。要输入此方法,线程1将获取alphonse的锁定,因为bow()
方法已同步。gaston.bow()
。要输入此方法,线程2将获取gaston锁,因为bow()
方法已同步。gaston.bowBack()
。要输入此方法,线程1需要获取gaston锁,因为bowBack()
方法已同步。它一直等到线程2释放了gaston的锁定alphonse.bowBack()
。要输入此方法,线程2需要获取alphonse的锁,因为bowBack()
方法是同步的。它一直等到线程1释放了alphonse的锁定两个线程最终等待彼此。这是一个僵局。
答案 1 :(得分:3)
想象两个线程并行执行:
thread1 thread2
----------------------------------- -----------------------------------
enter alphonse.bow(gaston) enter gaston.bow(alphonse)
- acquire lock on alphonse - acquire lock on gaston
gaston.bowBack(alphonse) alphonse.bowBack(gaston)
- try to acquire lock on gaston; - try to acquire lock on alphonse;
blocked because of gaston.bow() blocked because of alphonse.bow()
此时,两个线程都在等待另一个线程释放锁定,并且两者都无法完成同步方法,因此它将释放锁定(因为它正在等待另一个)。
答案 2 :(得分:2)
你有一个循环的锁链: alphonse.bow获得了对阿方斯的锁定,然后试图采取可能已经采取的gaston。 gaston.bow获得了对gaston的锁定,然后试图接受alphonse,但它已经被拿走了。
要分析/可视化,请绘制线程图(例如两个圆圈)和资源(比如两个矩形)。当线程请求资源从线程到资源绘制箭头时。授予资源后,从该资源向所有者线程绘制一个箭头。 只要你没有循环,一切都很好。 如果你最终得到一个循环,那么你需要在该循环中牺牲一些东西来打破循环
答案 3 :(得分:0)
这是 经典死锁场景:你有N个对象,每个对象都有自己的锁,你可以按随机顺序获取这些锁的多个。
当两个踏板试图“弯曲”时会出现问题。对彼此。每个线程都为自己的人(alphonse和gaston)获取锁定,然后尝试获取朋友的锁定,而仍然锁定自己的人。
这可以通过不同方式解决。
最简单的方法是通常使用全局锁(例如,在Friend类中定义的静态锁定对象)。这通常只允许一个线程一次执行bow / bowback序列。有时这很好,有时它可能是性能瓶颈。
更复杂的解决方案是确保以确定性顺序获取锁,例如,确保在锁定gaston之前始终获取alphonse的锁定。为此,您可以为要锁定的对象引入排序标准,为简单起见,我们假设我们可以依赖于朋友的名称是唯一的。
这使得实现有点复杂,但允许比全局锁更多并发,同时仍然避免死锁。盲目同步" self"该方法现在决定在哪个同步的优先顺序:
public void bow(Friend bower) {
// determine locking order
Friend first, second;
if (getName().compareTo(bower.getName()) > 0) {
first = this;
second = bower;
} else {
first = bower;
second = this;
}
synchronized (first) {
synchronized (second) {
// perform bow normally
}
}
}
这是有效的,因为无论alphonse是否向gaston鞠躬,反之亦然,他们每个人都会以相同的顺序获取锁。