谁能解释一下:
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();
}
}
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();
}
}
线程1:alphonse
实例被锁定从alphonse.bow(gaston);
打印一行,然后调用gaston.bowBack()
(但gaston
被锁定从线程2,因为下面调用了同步的bow()
实例)
线程2:gaston
实例被锁定从gaston.bow(alphonse);
打印一行,然后调用alphonse.bowBack()
(但由于调用同步的alphonse
实例,bow()
从线程1锁定)
所以他们都在等待释放,不能退出bow()
方法,因此死锁
首先,synchronized的使用是错误的。 oracle tutorial很好地说:
首先,对同一对象的两个同步方法的调用不可能进行交错。
正如另一个答案所解释的那样:示例中显示的代码不使用“公共锁”(两个不同对象上的同步方法不会影响“其他”方法调用)。
除此之外:一旦你删除那些System.out.format()
调用 - 你的程序可以(最常见的)不会遇到死锁。
或者:在启动线程之前将println()
放在main中 - 再次,程序不会死锁。
换句话说:打印到控制台非常耗时。因此,这会极大地影响线程的时间!这里发生的是大部分时间花在那些控制台输出操作上。请参阅here以获得甚至使用相同名称的类似问题;-)
您的示例中会发生什么:
bow
获取对Alphonse的锁定。bow
获取对加斯顿的锁定。bowBack
,但该锁目前由Thread 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
,因为否则在执行期间(通过代码中的错误或疏忽)可能会更改同步锁定,这会使您回到死锁状态,原因与上述完全相同。