Java中的重新入门使我们免于此代码示例中的死锁情况。如何,为什么?

时间:2014-02-03 02:42:56

标签: java multithreading reentrancy

我是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();
   }
}
  

并且线程将永久停止等待锁定它   永远无法获得。

如何,为什么,哪个线程?

3 个答案:

答案 0 :(得分:3)

  

如何,为什么,哪个线程?

死锁的线程是尝试获取锁的线程;即这一个。


如何:

  1. 获取对LoggingWidget实例

  2. 的引用
  3. 在实例

  4. 上调用doSomething()
  5. LoggingWidget.doSomething()的调用获取了实例上的锁定,因为该方法为synchronized

  6. 然后,被调用的方法调用super.doSomething()

  7. Widget.doSomething()的调用尝试获取实例上的锁(再次!),因为超类型方法也是synchronized

  8. 在第5步。当前线程尝试在已锁定的实例上获取原始锁。如果原始锁不是可重入的,那么就会陷入僵局......

    • “某个线程”已经持有对象的锁,所以我们必须等待该线程释放锁,

    • “某个线程”是当前线程......我们不会在从LoggingWidget.doSomething()

    • 返回之前释放锁定
    • 但在我们完成对Widget.doSomething()

    • 的调用之后才能发生这种情况
    • 直到我们获得锁定

    • 之后才能发生这种情况
    • ..... DEADLOCK!

    但幸运的是,现实中没有僵局。原始锁是可重入的这一事实意味着步骤#5不需要获取锁(它已经拥有它),以及整个等待我自己做的事情 - 那个 - 不可能发生的情况根本不会出现。


    为什么:因果关系不可避免的力量: - )

答案 1 :(得分:1)

我认为你对作者使用re-entrancy一词感到困惑。通过“锁是可重入的”,他意味着如果你的线程已经拥有一个锁,那么Java允许从另一个synchronized方法中输入synchronized方法,或者重新输入相同的synchronized方法递归。

这与重新引入的一个更常见的含义不同,即在保留状态时,一段代码重新输入,同时或在同一线程上的能力。

如果不重新考虑作者的意思,synchronized的{​​{1}}会阻止试图获取super.doSomething()所持有的锁,因为它们会阻挡同一个对象(即在this.doSomething())。

答案 2 :(得分:0)

你有两个锁定同一个对象的方法 - 一个LoggingWidget的实例 - 它将由同一个线程运行。

  1. 创建LoggingWidget的实例
  2. 针对该实例
  3. 调用doSomething类中定义的LoggingWidget方法
  4. 获取LoggingWidget实例的锁定
  5. 执行println语句
  6. doSomething类的LoggingWidget方法调用doSomething类中定义的Widget方法
  7. 需要对LoggingWidget的同一个实例进行锁定才能继续,但是执行第一个方法的线程已经将其保留了
  8. 如果锁不可重入,则在步骤6发生死锁,因为实例上的锁已经被保持。但由于Java中的内置Object锁是可重入的,因此步骤6正常继续,因为执行的线程已经保存了所需的锁。