这是java docs for deadlock的教程。我没有得到线程被阻止的地方。因为它同步我以为只有一个线程会进入弓形。但两人都进了弓。 [等待[但什么时候?]]
那么问题出在哪里?
当我添加注释[print statements to trace]时。没有僵局。如何?
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() {
@Override
public void run() {
// System.out.println("Thread 1");
alphonse.bow(gaston);
// System.out.println("Th: gaston bowed to alphonse");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// System.out.println("Thread 2");
gaston.bow(alphonse);
// System.out.println("2.gaston waiting alph bowed");
}
}).start();
}
}
输出结果为:
Alphonse: Gaston has bowed to me!
Gaston: Alphonse has bowed to me!
没有人回头!
答案 0 :(得分:6)
这里要理解两件重要的事情:1)每个并发运行的线程在做什么? 2)涉及哪些锁?
最后,你创建了两个Friend类的实例:alphonse和gaston。每个对象都拥有自己的锁。所以有两个锁:alphonse的锁和gaston的锁。当您输入对象的同步方法时,它会锁定该对象的锁定。当synchronized方法返回时,锁被释放(解锁)。
现在是线程。你的第一个线程,让我们称之为Alphone的线程(注意大写A来区分线程和对象,alphonse)这样做:(“A:”表示这是Alphonse的线程。)
A: alphonse.bow(gaston) - acquires alphonse's lock
A: gaston.bowBack(alphonse) - acquires gaston's lock
A: both methods return, thus releasing both locks
同时,加斯顿的主题是:(“G:”表示这是加斯顿的主题。)
G: gaston.bow(alphonse) - acquires gaston's lock
G: alphonse.bowBack(gaston) - acquires alphonse's lock
G: both methods return, thus releasing both locks
现在我们可以将这两位信息放在一起以得出答案。线程可以以不同的顺序交错(即,它们的事件发生)。例如,如果事件按此顺序发生,则会发生死锁:
A: alphonse.bow(gaston) - acquires alphonse's lock
G: gaston.bow(alphonse) - acquires gaston's lock
G: attempts to call alphonse.bowBack(gaston), but blocks waiting on alphonse's lock
A: attempts to call gaston.bowBack(alphonse), but blocks waiting on gaston's lock to
在这种情况下,每个线程都被阻塞,等待获取另一个线程持有的锁。但是,两个线程都不会释放它所持有的锁,因为它必须完成它的方法才能这样做,因为它被阻塞等待另一个线程所以它无法完成。因此它们被卡住了 - 僵持不下。
然而,另一种可能的交错(事件顺序)是其中一个线程在另一个线程真正开始之前完成的,例如:
A: alphonse.bow(gaston) - acquires alphonse's lock
A: gaston.bowBack(alphonse) - acquires gaston's lock
A: both methods return, thus releasing both locks
G: gaston.bow(alphonse) - acquires gaston's lock
G: alphonse.bowBack(gaston) - acquires alphonse's lock
G: both methods return, thus releasing both locks
在这种情况下,没有死锁。当您添加println并且没有死锁时,很可能发生的事情是额外println的额外延迟会影响线程运行的调度和/或速率,因此您可以获得这种交错而不是死锁。
当结果取决于并发事件的顺序(即,它们的调度或它们运行的速率)时,称为“竞争条件”。例如,在该程序中,哪个线程首先打印输出取决于它们的调度顺序或它们运行的速率。线程死锁是否也取决于此。因此,通过扰乱该比率(例如,通过添加额外指令),您可能会影响比赛的结果 - 但这并不意味着比赛已经消失。
并非所有竞争条件都有可能导致死锁。但是,根据我的经验,死锁只发生在竞争条件下。 (也许知道的人可以评论是否一定是这种情况,或者通常是这种情况?)
一开始就不容易理解死锁;我尽力解释,但如果您对我的帖子有任何后续问题,请告诉我。 :)
答案 1 :(得分:5)
首先,调用System.out.println()
会带来大量资源,因此会在处理过程中产生有效延迟,从而人为地导致/避免并发问题。不要过多地了解添加打印效果。
现在,当线程A锁定X然后锁定Y时发生死锁,并且同时线程B锁定Y然后锁定X.如果两者都得到它们的第一个锁,则两者都不能继续=死锁。
在你的情况下,方法bow()
锁定它被调用的实例,但是然后从该锁中调用bowee的bow方法:同步锁定实例,所以两个单独的实例都可以获得第一次锁定。第二个锁在其他实例上,因此两个线程都在等待另一个thread = deadlock持有的锁。
答案 2 :(得分:0)
你有两个独立的对象 - synchronized关键字不会阻止在对象1中同时在对象2中调用bow。它只会阻止两个线程进入同一个对象的synchronized方法
然后输入两个方法,每个对象都有锁。然后,两者都不能进入另一个对象的方法,因为同步关键字在对象级别上工作。
答案 3 :(得分:0)
在某个时间点考虑,“Alphonse”和“Gaston”都开始鞠躬,这意味着他们已经分别拥有了自己的锁。然后他们都希望另一个同时向后退,这意味着他们两个都试图获得对方的锁定。由于两个锁都被占用,bowBack()
呼叫将被阻止。由于bowBack()
位于bow()
块内,因此bow()
函数无法立即返回。因此,当他们已经获得一些资源时,他们处于僵局状态,但仍在尝试获取已经采取的其他资源。
答案 4 :(得分:-2)
Here准确地解释了。由于第一个对象试图在第二个对象上调用某个东西,因此第二个对象在等待同步块时等待,因此对bowBack()的第一次调用将永远不会结束,并且两个调用都会被卡在那里。阅读文章。我第一次没有注意到你从对面的对象那里打电话给bower.getName()
。 bower.getName()
调用导致死锁