我正在学习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的解释:
当Deadlock运行时,它是非常的 可能两个线程都会阻塞 当他们试图调用bowBack时。 这两个块都不会结束,因为 每个线程都在等待另一个 退出弓。
我似乎不太关注。当alphonse.bow(gaston)跑动时,弓法被锁定。所以现在它首先打印“加斯顿向我鞠躬!”。然后它会继续调用bowBack,并锁定bowBack。这怎么会导致死锁?我是否误解了调用同步方法时会发生什么?
如果有人能给我一个简单的解释,谢谢。
答案 0 :(得分:27)
需要注意的一点是,方法不是锁定的,而是对象实例。
当您致电alphonse.bow(gaston)
时,它会尝试获取alphonse
上的锁定。一旦锁定,它就会打印一条消息,然后调用gaston.bowBack(alphonse)
。此时,它尝试获取gaston
上的锁定。一旦锁定,它就会打印一条消息,然后释放锁定,最后释放alphonse
上的锁定。
在死锁中,锁的获取顺序是任何一个线程都无法继续进行。
alphonse
gaston
gaston
上的锁定 - 不能,因为线程2已经拥有它。alphonse
上的锁定 - 不能,因为线程1已经拥有它。答案 1 :(得分:8)
alphonse 和 gaston 是两个不同的对象。每个对象都有一个与之关联的内在监视器(锁)。
可能会发生这样的事情:
alphonse已创建。他的对象监视器是1。
gaston已创建。他的对象监视器是2。
alphonse.bow(Gaston)的; alphonse现在拥有锁#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());
}
}
}
}
答案 2 :(得分:1)
锁定保存在Java对象上,而不是Java方法上。因此,当在方法上使用synchronized时,它会锁定“this”对象。在静态方法的情况下,它锁定类对象。
您可以使用synchronized ( object ) { }
答案 3 :(得分:1)
添加到simonn等人,
如果您想要将其可视化,请编译并运行该程序并生成正在运行的程序的线程转储。您可以通过在Windows控制台中键入 Ctrl - Break 或向* nix系统发出kill -QUIT [pid]
命令来完成此操作。这将为您提供一个列表,列出系统中运行的所有Threads
以及它们正在运行或等待的位置,以及线程锁定或等待锁定的监视器。
如果在构造函数中更改了Thread名称,则可以更容易地在完整的线程转储中找到它们:
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}, "Alphonse").start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}, "Gaston").start();
答案 4 :(得分:0)
同步是对象(this)本身同步的简写。实质上,这意味着不能在相同的Friend对象上相互调用bow()和bowBack()。
现在如果两个朋友都进入bow(),他们都不会调用彼此的bowBack()方法。
答案 5 :(得分:-3)
我必须仔细检查,但我认为同步方法会锁定类对象,因此它会锁定同一类中的其他同步方法。
我认为它会锁定类对象本身,因此它甚至会阻止不同的实例。
编辑添加:
的这一部分每个弓法都抓住它自己的物体监视器。然后两者都试图将另一个对象的弓向后调用,并阻止等待另一个监视器。