关于Java中死锁情况的问题

时间:2009-10-13 20:23:26

标签: java concurrency deadlock

我正在学习Java中的死锁,并且有来自Sun官方教程的示例代码:

  

Alphonse和Gaston是朋友,而且   很有礼貌的信徒。一个严格的   礼貌的规则就是你鞠躬时   对朋友,你必须保持鞠躬   直到你的朋友有机会   归还弓。不幸的是,这个   规则不占   两个朋友可能会低头的可能性   彼此同时。

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();
    }
}

以下是Sun的解释:

  

当Deadlock运行时,它是非常的   可能两个线程都会阻塞   当他们试图调用bowBack时。   这两个块都不会结束,因为   每个线程都在等待另一个   退出弓。

我似乎不太关注。当alphonse.bow(gaston)跑动时,弓法被锁定。所以现在它首先打印“加斯顿向我鞠躬!”。然后它会继续调用bowBack,并锁定bowBack。这怎么会导致死锁?我是否误解了调用同步方法时会发生什么?

如果有人能给我一个简单的解释,谢谢。

6 个答案:

答案 0 :(得分:27)

需要注意的一点是,方法不是锁定的,而是对象实例

当您致电alphonse.bow(gaston)时,它会尝试获取alphonse上的锁定。一旦锁定,它就会打印一条消息,然后调用gaston.bowBack(alphonse)。此时,它尝试获取gaston上的锁定。一旦锁定,它就会打印一条消息,然后释放锁定,最后释放alphonse上的锁定。

在死锁中,锁的获取顺序是任何一个线程都无法继续进行。

  • 主题1:获取对alphonse
  • 的锁定
  • 主题2:获取对gaston
  • 的锁定
  • 主题1:打印消息
  • 线程1:尝试获取gaston上的锁定 - 不能,因为线程2已经拥有它。
  • 主题2:打印消息
  • 线程2:尝试获取alphonse上的锁定 - 不能,因为线程1已经拥有它。

答案 1 :(得分:8)

alphonse gaston 是两个不同的对象。每个对象都有一个与之关联的内在监视器(锁)。

可能会发生这样的事情:

alphonse已创建。他的对象监视器是1。

gaston已创建。他的对象监视器是2。

alphonse.bow(Gaston)的; alphonse现在拥有锁#1

gaston.bow(阿尔); 加斯顿现在拥有锁#2

alphonse在gaston上调用bowBack并等待锁定#2 gaston在alphonse上调用bowBack并等待锁定#1

有意义吗?使用实例监视方法持续时间的synchronized关键字锁。该示例可以重写如下:

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        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 void bowBack(Friend bower) {
            synchronized(this) {
                        System.out.format("%s: %s has bowed back to me!%n",
                    this.name, bower.getName());
            }
        }
    }
}

答案 2 :(得分:1)

锁定保存在Java对象上,而不是Java方法上。因此,当在方法上使用synchronized时,它会锁定“this”对象。在静态方法的情况下,它锁定类对象。

您可以使用synchronized ( object ) { }

显式指定监视器对象

答案 3 :(得分:1)

添加到simonn等人,

如果您想要将其可视化,请编译并运行该程序并生成正在运行的程序的线程转储。您可以通过在Windows控制台中键入 Ctrl - Break 或向* nix系统发出kill -QUIT [pid]命令来完成此操作。这将为您提供一个列表,列出系统中运行的所有Threads以及它们正在运行或等待的位置,以及线程锁定或等待锁定的监视器。

如果在构造函数中更改了Thread名称,则可以更容易地在完整的线程转储中找到它们:

   new Thread(new Runnable() {
        public void run() { alphonse.bow(gaston); }
    }, "Alphonse").start();
    new Thread(new Runnable() {
        public void run() { gaston.bow(alphonse); }
    }, "Gaston").start();

答案 4 :(得分:0)

方法定义中的

同步是对象(this)本身同步的简写。实质上,这意味着不能在相同的Friend对象上相互调用bow()和bowBack()。

现在如果两个朋友都进入bow(),他们都不会调用彼此的bowBack()方法。

答案 5 :(得分:-3)

我必须仔细检查,但我认为同步方法会锁定类对象,因此它会锁定同一类中的其他同步方法。

我认为它会锁定类对象本身,因此它甚至会阻止不同的实例。

编辑添加:

看一下java language spec

的这一部分

每个弓法都抓住它自己的物体监视器。然后两者都试图将另一个对象的弓向后调用,并阻止等待另一个监视器。