死锁 - 在这个例子中它是如何发生的?

时间:2017-08-09 07:50:25

标签: java multithreading deadlock synchronized

任何人都可以解释:

  1. 为什么我们会遇到僵局?
  2. Gaston怎么能在Alphonse退出之前进入功能弓? (它应该从函数bowBack()返回以退出函数bow() - 或者)?
  3. 这是我得到的输出 - 然后程序卡住了!

      

    阿方斯:加斯顿向我鞠躬!

         加斯顿:阿尔方斯向我鞠躬!

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

4 个答案:

答案 0 :(得分:23)

synchronized块/方法与this同步,即调用块/方法的对象实例。 (对于static"对象实例"将替换为"类实例"。)

这就是你的两个对象与自己同步,而不是一个共同的对象。

尝试这样的事情:

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 (getClass()) {
            System.out.format("%s: %s  has bowed to me!%n", this.name, bower.getName());
            bower.bowBack(this);
         }
      }
      public void bowBack(Friend bower) {
         synchronized (getClass()) {
            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();
   }
}

答案 1 :(得分:9)

主题1: alphonse实例从alphonse.bow(gaston);锁定,后者打印一行,然后调用gaston.bowBack()(但gaston由于调用同步bow()实例而从线程2锁定下文)

主题2: gaston实例从gaston.bow(alphonse);锁定,后者打印一行,然后调用alphonse.bowBack()(但alphonse由于调用同步bow()实例而从线程1锁定)

所以他们都在等待发布并且无法退出bow()方法,因此死锁

答案 2 :(得分:6)

首先, synchronized 用法是错误的。 oracle tutorial很好地陈述:

  

首先,相同对象上的两个同步方法调用不可能进行交错。

正如另一个答案所解释的那样:示例中显示的代码不使用"公共锁定" (两个不同对象上的同步方法不会影响"其他"方法调用)。

除此之外:只要您删除那些System.out.format()来电,您的程序就会(通常会)而不是遇到死锁。

或者:在之前加上println() 开始执行主题 - 再次,程序将死锁。

换句话说:打印到控制台非常耗时。因此,这会极大地影响线程的时间!这里发生的是大多数时间用于那些控制台输出操作。有关类似问题甚至使用相同的名称,请参阅here; - )

答案 3 :(得分:5)

您的示例中会发生什么:

  1. 线程Alphonse通过输入函数bow获取对Alphonse的锁定。

  2. 线程加斯顿通过输入bow函数获取对加斯顿的锁定。

  3. 线程Alphonse请求锁定加斯顿进入函数bowBack但该锁目前由Thread Gaston持有,因此Alphonse被迫等待。

  4. 线程Gaston请求锁定Alphonse进入函数bowBack,但该锁定目前由Thread Alphonse持有,因此Gaston被迫等待。

  5. 死锁。

    为什么会这样:

    synchronized函数是synchronized(this) { ... }的语法糖所以上面的类也可以这样编写:

    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());
        }
    }
    
    但是,此示例中的

    this是类的实例,因此每个实例都具有单独的锁。如果要在类的所有实例中锁定同一对象,则需要锁定这样的静态对象:

    protected static final Object STATIC_LOCK = new Object();
    
    public void bow(Friend bower) {
        synchronized (STATIC_LOCK) {
            System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName());
            bower.bowBack(this);
        }
    }
    
    public void bowBack(Friend bower) {
        synchronized (STATIC_LOCK) {
            System.out.format("%s: %s has bowed back to me!%n", this.name, bower.getName());
        }
    }
    

    由于此LOCK对象是静态的,因此两个线程现在将锁定在相同的对象上,因此会正确锁定对方。请注意在这种情况下强烈建议的关键字final,因为否则在执行期间(通过代码中的错误或疏忽)可能会更改同步锁定,这会使您回到死锁状态与上述原因相同。