【问题标题】:How Deadlock arises in the given Java program? [duplicate]在给定的 Java 程序中死锁是如何产生的? [复制]
【发布时间】:2014-04-10 16:03:03
【问题描述】:

以下代码 sn-p 取自 Java Tutorials,它表示线程永远保持阻塞状态,但我无法弄清楚如何。请解释一下。

两个同步方法是否共享相同的内在锁?

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

【问题讨论】:

  • 如果你在这个网站上搜索“alphonse gaston”,你会发现你的问题已经被问了几十次了。
  • 请注意,教程中说:“两个线程在尝试调用 bowBack 时会阻塞”(重点是我的)。所以每次运行都不能保证死锁。

标签: java synchronization deadlock


【解决方案1】:

是的,这两种方法都有相同的锁(我猜是对象本身),因为它们声明here:

当一个线程为一个对象执行同步方法时,所有 为同一对象调用同步方法的其他线程 阻止

【讨论】:

    【解决方案2】:

    在一个线程的上下文中,方法弓在另一个线程的上下文中被称为方法,这就是问题。

    Alphonse 和 Gaston 的这个对象是不同的,同步是在两个线程中按顺序在这两个上完成的。第一个进入的线程会先拿第一把锁,然后再尝试拿第二把,但由于其他线程已经拿走了其他锁,它会被停止。

    如果一个线程等待另一个线程完成完整计算,则不会出现问题。

    【讨论】:

      【解决方案3】:

      在上面的示例中可能有以下情况:

      alphonse has his own lock
      gaston has his own lock
      

      因为两者都使用自己的锁在自己的线程中调用bow() 方法。如果您仔细观察,在 bow() 方法中,alphonse 现在使用另一个引用(即使用 gaston 的锁)调用 bowback()。

      因此,可能有这样一种情况,gaston 已经使用他自己的锁进入了bow() 方法,但 alphonse 的线程试图进入已经被 gaston 的线程锁定的bowback() 方法。反之亦然。

      因此,调用bowback() 方法的引用反转很容易出错,这可能导致死锁。

         public synchronized void bow(Friend bower) {
              System.out.format("%s: %s"
                  + "  has bowed to me!%n", 
                  this.name, bower.getName());
              bower.bowBack(this); // <---------  This Line 
              // here bower is not the same person how acquired
              // the lock while calling bow() method
          }
      

      【讨论】:

        【解决方案4】:

        线程 A 在 bow(gaston) 时为 alphonse 获得锁 当bow(aphonse) 时,线程 B 为 gaston 获得锁 当bowBack(gaston)时线程A等待锁 bowBack(aphonse)时线程B等待锁

        【讨论】:

          【解决方案5】:

          来自您的链接:

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

          什么是死锁?

          通常当一个线程 (1) 锁定一个资源 (A) 然后尝试锁定另一个资源 (B),但是资源 B 被另一个试图锁定资源 A 的线程 (2) 锁定时,就会发生死锁。

          在这种情况下,没有线程会细化对这两个资源的访问,并且会一直等待直到资源空闲。

          Jamie 注意到,如果幸运的话,不会发生死锁,但有了 i7,我就明白了

          Alphonse: Gaston  has bowed to me!
          Gaston: Alphonse  has bowed to me!
          

          记住synchronized在方法中的意思是synchronized(this)。

          作为@Nikita Rybak 在同一个问题中的回答 (Java deadlock question)(支持他的回答!)

          1. 输入 alphonse.bow(gaston);,alphonse 现在由于同步关键字而被锁定
          2. 输入 gaston.bow(alphonse);,gaston 现已锁定
          3. 无法执行 bower.bowBack(this);从第一个弓法调用开始,因为加斯顿(鲍尔)被锁定。等待锁被释放。
          4. 无法执行 bower.bowBack(this);从第二个弓方法调用,因为 alphonse (bower) 被锁定。等待锁被释放。

          其他答案:

          How this java code produces deadlock?

          Java deadlock question

          【讨论】:

            猜你喜欢
            • 2012-01-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-07-31
            • 2013-12-28
            • 1970-01-01
            • 1970-01-01
            • 2013-11-27
            相关资源
            最近更新 更多