Java死锁代码说明

时间:2014-08-14 16:14:37

标签: java multithreading

有人可以解释为什么以下代码导致死锁。 我的理解是,当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();
}

}

4 个答案:

答案 0 :(得分:6)

  1. 主题1:alphonse.bow()。要输入此方法,线程1将获取alphonse的锁定,因为bow()方法已同步。
  2. 主题2:gaston.bow()。要输入此方法,线程2将获取gaston锁,因为bow()方法已同步。
  3. 主题1:gaston.bowBack()。要输入此方法,线程1需要获取gaston锁,因为bowBack()方法已同步。它一直等到线程2释放了gaston的锁定
  4. 主题2:alphonse.bowBack()。要输入此方法,线程2需要获取alphonse的锁,因为bowBack()方法是同步的。它一直等到线程1释放了alphonse的锁定
  5. 两个线程最终等待彼此。这是一个僵局。

答案 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鞠躬,反之亦然,他们每个人都会以相同的顺序获取锁。