我是Java和OOP的新手。我正在阅读java中的并发性,在第2章中,它讨论了重新进入。我不太明白如何发生僵局。有人可以打破这种情况,让我逐行了解细节吗?
提前谢谢你。
如果内部锁不可重入,则调用super.doSomething 永远无法获得锁定,因为它会 被认为已经举行,线程将永久停止 等待它永远无法获得的锁定。
public class Widget {
public synchronized void doSomething() {
...
}
}
public class LoggingWidget extends Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}
并且线程将永久停止等待锁定它 永远无法获得。
如何,为什么,哪个线程?
答案 0 :(得分:3)
如何,为什么,哪个线程?
死锁的线程是尝试获取锁的线程;即这一个。
如何:
获取对LoggingWidget
实例
在实例
doSomething()
对LoggingWidget.doSomething()
的调用获取了实例上的锁定,因为该方法为synchronized
。
然后,被调用的方法调用super.doSomething()
。
对Widget.doSomething()
的调用尝试获取实例上的锁(再次!),因为超类型方法也是synchronized
。
在第5步。当前线程尝试在已锁定的实例上获取原始锁。如果原始锁不是可重入的,那么就会陷入僵局......
“某个线程”已经持有对象的锁,所以我们必须等待该线程释放锁,
“某个线程”是当前线程......我们不会在从LoggingWidget.doSomething()
但在我们完成对Widget.doSomething()
直到我们获得锁定
..... DEADLOCK!
但幸运的是,现实中没有僵局。原始锁是可重入的这一事实意味着步骤#5不需要获取锁(它已经拥有它),以及整个等待我自己做的事情 - 那个 - 不可能发生的情况根本不会出现。
为什么:因果关系不可避免的力量: - )
答案 1 :(得分:1)
我认为你对作者使用re-entrancy一词感到困惑。通过“锁是可重入的”,他意味着如果你的线程已经拥有一个锁,那么Java允许从另一个synchronized
方法中输入synchronized
方法,或者重新输入相同的synchronized
方法递归。
这与重新引入的一个更常见的含义不同,即在保留状态时,一段代码重新输入,同时或在同一线程上的能力。
如果不重新考虑作者的意思,synchronized
的{{1}}会阻止试图获取super.doSomething()
所持有的锁,因为它们会阻挡同一个对象(即在this.doSomething()
)。
答案 2 :(得分:0)
你有两个锁定同一个对象的方法 - 一个LoggingWidget
的实例 - 它将由同一个线程运行。
LoggingWidget
的实例doSomething
类中定义的LoggingWidget
方法
LoggingWidget
实例的锁定println
语句doSomething
类的LoggingWidget
方法调用doSomething
类中定义的Widget
方法LoggingWidget
的同一个实例进行锁定才能继续,但是执行第一个方法的线程已经将其保留了如果锁不可重入,则在步骤6发生死锁,因为实例上的锁已经被保持。但由于Java中的内置Object
锁是可重入的,因此步骤6正常继续,因为执行的线程已经保存了所需的锁。