任何人都可以解释:
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();
}
}
答案 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)
您的示例中会发生什么:
线程Alphonse通过输入函数bow
获取对Alphonse的锁定。
线程加斯顿通过输入bow
函数获取对加斯顿的锁定。
线程Alphonse请求锁定加斯顿进入函数bowBack
但该锁目前由Thread Gaston持有,因此Alphonse被迫等待。
线程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());
}
}
但是,此示例中的 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
,因为否则在执行期间(通过代码中的错误或疏忽)可能会更改同步锁定,这会使您回到死锁状态与上述原因相同。