关于Java中死锁情况的问题

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

阿方斯和加斯顿是朋友,也很有礼貌的信徒。 严格的礼貌规则是,当你向朋友鞠躬时,你必须保持鞠躬,直到你的朋友有机会归还弓。 不幸的是,这条规则没有考虑到两个朋友可能同时互相鞠躬的可能性。

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。 这怎么会导致僵局? 我是否误解了调用同步方法时会发生什么?

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

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

当你打电话给alphonse.bow(gaston) ,它试图获得对alphonse的锁定。 一旦它有锁,它会打印一条消息,然后调用gaston.bowBack(alphonse) 。 此时,它试图获取gaston的锁定。 一旦锁定,它就会打印一条消息,然后释放锁定,最后释放对alphonse的锁定。

在死锁中,以这样的顺序获取锁,使得任一线程都无法继续。

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

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

可能会发生这样的事情:

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

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

alphonse.bow(Gaston)的; 阿尔方斯现在拥有锁#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()); } } } } 

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

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

添加到simonn等人,

如果您想要将其可视化,请编译并运行该程序并生成正在运行的程序的线程转储。 您可以通过在Windows控制台上键入CtrlBreak或向* 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(); 

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

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

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

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

编辑添加:

看一下java语言规范的这一部分

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