繁体   English   中英

有关Java死锁情况的问题

[英]Question About Deadlock Situation in Java

我正在学习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的解释:

死锁运行时,两个线程极有可能在尝试调用bowBack时阻塞。 两个块都不会结束,因为每个线程都在等待另一个退出弓。

我似乎不太了解。 当alphonse.bow(gaston)运行时,bow方法被锁定。 因此,现在它将首先打印“ Gaston向我鞠躬!”。 然后它将继续并调用bowBack,并同时锁定bowBack。 这怎么会造成僵局? 我是否误解了调用同步方法时会发生什么?

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

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

当您调用alphonse.bow(gaston) ,它将尝试获取对alphonse的锁定。 拥有锁后,它将打印一条消息,然后调用gaston.bowBack(alphonse) 在这一点上,它试图获得gaston的锁定。 拥有锁后,它将打印一条消息,然后释放该锁,最后释放alphonse上的锁。

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

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

阿方斯加斯东是两个不同的对象。 每个对象都有一个与其关联的内部监视器(锁)。

可能会这样发生:

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

加斯顿创建。 他的对象监视器是2。

alphonse.bow(Gaston)的; 阿方斯现在拥有#1号锁

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

阿方斯在加斯顿上呼叫bowBack并等待锁#2阿方在加斯顿上呼叫bowBack并等待锁#1

说得通? 使用实例在此方法期间监视的synced关键字锁。 该示例可以重写如下:

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

锁持有Java对象,而不是Java方法。 因此,当在方法上使用sync时,它将锁定“ this”对象。 对于静态方法,它将锁定类对象。

您可以使用synchronized ( object ) { }显式指定监视对象。

要补充到simonn等人,

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

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

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

在方法定义中同步是在对象本身上进行同步的简写。 从本质上讲,这意味着不能在同一个Friend对象上相互调用bow()和bowBack()。

现在,如果两个Friends都进入bow(),那么他们两个都将无法调用彼此的bowBack()方法。

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

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

编辑添加:

看看Java语言规范的这一部分

每个bow方法都拥有自己的对象监视器。 然后两者都尝试调用另一个对象的弓箭,并阻止其等待另一个监视器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM