[英]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
上的锁。
在死锁中,锁的获取顺序使得任何一个线程都无法继续执行。
alphonse
gaston
gaston
锁-不能,因为线程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.