谁能解释一下:
- 为什么会出现僵局?
- 加斯顿如何在艾尔冯斯退出该功能之前进入功能弓?(它应该从函数
bowBack()
返回以退出函数bow()
- OR(?
这是我得到的输出 - 然后程序卡住了!
艾尔冯斯:"加斯顿向我鞠躬了!
加斯顿: 艾尔冯斯向我鞠躬了!
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()
(但由于下面调用的实例同步bow()
gaston
线程 2 被锁定(
线程 2:gaston
实例被锁定,gaston.bow(alphonse);
打印一行然后调用alphonse.bowBack()
(但由于同步bow()
实例调用alphonse
线程 1 被锁定(
所以他们都在等待发布并且无法退出bow()
方法,因此死锁
首先,同步的用法是错误的。预言机教程很好地指出:
首先,对同一对象上的同步方法进行两次调用不可能交错。
正如另一个答案所解释的:示例中显示的代码不使用"公共锁"(两个不同对象上的同步方法不会影响"其他"方法调用(。
除此之外:一旦你删除了这些System.out.format()
调用 - 你的程序可以(通常(不会陷入死锁。
或者:在启动线程之前,在主线程中放一个println()
- 同样,程序不会死锁。
换句话说:打印到控制台非常耗时。因此,这会极大地影响线程的时间!这里发生的情况是,大部分时间都花在这些控制台输出操作上。有关甚至使用相同的名称的类似问题,请参阅此处;-(
在你的例子中发生了什么:
-
线程阿尔方斯正在通过输入功能
bow
来获取阿尔方斯的锁。
线程加斯顿 正在通过输入功能
bow
来获取加斯顿的锁。线程阿尔方斯正在向加斯顿请求锁以进入功能
bowBack
但该锁目前由线程加斯顿持有,因此艾尔方斯被迫等待。线程加斯顿正在向阿尔方斯请求锁以进入功能
bowBack
但该锁目前由线程阿尔方斯持有,因此加斯顿被迫等待。
死锁。
为什么会这样:
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
,因为否则同步锁可能会在执行期间更改(通过代码中的错误或疏忽(,这会让您重新陷入死锁状态,原因与上述完全相同。