问题 可重入锁定


请帮助一下,考虑下面的代码。

public class Widget {
    public synchronized void doSomething() {
        ...
    }
}

public class LoggingWidget extends Widget {
    public synchronized void doSomething() {
        System.out.println(toString() + ": calling doSomething");
        super.doSomething();
    }
}

我读到当调用LoggingWidget中的doSomething()时,JVM将首先尝试获取LoggingWidget上的锁定,然后尝试获取Widget上的锁定。

我很想知道原因。是因为JVM知道doSomething()调用了super.doSomething(),或者因为调用子类方法总是会获得对超类的锁定。

干杯


3723
2017-07-09 15:37


起源

你应该发布你读到这里的引用,因为它不是真的:-) - oxbow_lakes
谢谢你的帮助。我误解了折返锁定的解释。在阅读完解释后,我回到了源代码(摘自“实践中的并发”一书),现在确实有意义。 - CaptainHastings


答案:


你错了 - 锁是在  水平。您的示例中只有一个锁,因为只有当您说:

Widget w = new LoggingWidget

你可以查看锁(也称为 显示器互斥 要么 信号灯)作为单独“附加”到每个对象实例 JVM

如果你有另一个 synchronized 关于的方法 LoggingWidget 子类,你会看到这是真的。无法调用此(新)方法和 doSomething 方法同时[在同一对象上使用不同的线程]。

这也适用于另一个人 synchronized 超类上的方法(即它不会被覆盖的方法以任何方式影响)。


9
2017-07-09 15:40





public synchronized void doSomething() {
    System.out.println(toString() + ": calling doSomething");
    super.doSomething();
}

是相同的:

public void doSomething() {
    synchronized (this) {
        System.out.println(toString() + ": calling doSomething");
        super.doSomething();
    }
}

你锁定了实例,而不是类。因此,当调用super.doSomething()时,您已经锁定了该实例。


5
2017-07-09 15:46



谢谢,随着歌曲的流逝,我现在可以清楚地看到...... - CaptainHastings


只有一个实例可以锁定,实例是 LoggingWidget,从来没有一个实际的例子 Widget

您致电时获得锁定 LoggingWidget.doSomething() 当你打电话时已经锁定了 super.doSomething() 该方法正常执行。


1
2017-07-09 15:43



谢谢尼克,这很有道理。 - CaptainHastings


Reentrancy首先获得锁定。当一个线程获得锁时,它在jvm中是已知的。当输入与当前持有锁的线程同步的代码块时,允许它们继续通过而不重新获取锁。然后jvm每次重入动作都会增加一个计数器,当退出该块时直到计数为零时进一步减少。当计数为零时,锁定被释放。


0
2017-07-09 22:33





B.Goetz - “实践中的JJava并发”如果内部锁不可重入,对super.doSomething的调用将永远无法获取锁,因为它将被认为已被保留,并且线程将永久停止等待锁定它永远无法获得。在这种情况下,重入可以使我们免于死锁。


0
2017-07-10 11:49