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

Oha*_*had 22 java multithreading deadlock synchronized

谁能解释一下:

  1. 为什么我们陷入僵局?
  2. Gaston怎么能在Alphonse退出之前进入功能弓?(它应该从函数返回bowBack()以退出函数bow()- 或)?

这是我得到的输出 - 然后程序卡住了!

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

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

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();
    }
} 
Run Code Online (Sandbox Code Playgroud)

Usa*_*oto 23

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

那就是你的2个对象与自己同步,而不是常见的对象.

尝试这样的事情:

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();
   }
}
Run Code Online (Sandbox Code Playgroud)


isa*_*sah 9

线程1: alphonse实例被锁定,alphonse.bow(gaston);从中打印出一行然后调用gaston.bowBack()(但是gaston由于bow()下面调用的同步实例而被线程2锁定)

线程2: gaston实例被锁定,gaston.bow(alphonse);从中打印一行,然后调用alphonse.bowBack()(但alphonse由于bow()调用了同步实例,因此从线程1锁定)

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


Gho*_*ica 6

首先,在使用同步是错误的.oracle 教程很好地说明:

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

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

除此之外:一旦删除这些System.out.format()调用 - 您的程序可以(最常见)不会陷入死锁.

或者:println()在启动线程之前将一个放入主体中- 再次,程序不会死锁.

换句话说:打印到控制台非常耗时.因此,这会严重影响线程的时间!这里发生的是大部分时间花在那些控制台输出操作上.见这里的,即使是使用相同的名称类似的问题;-)


Two*_*The 5

您的示例中会发生什么:

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

  2. 线程加斯顿通过输入功能获取对加斯顿的锁定bow.

  3. 线程Alphonse请求锁定Gaston进入该功能,bowBack但该锁定目前由Thread Gaston持有,因此Alphonse被迫等待.

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

僵局.

为什么会这样:

一个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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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