可重入锁定

11

请帮个忙,考虑下面的代码段:

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()的调用吗?还是说调用子类方法总会在超类上获取锁呢?

谢谢


你应该发布一个引用链接,证明你所说的不是真的 :-) - oxbow_lakes
非常感谢您的帮助。我对可重入锁的解释有所误解。在阅读了您的解释后,我回到了源代码(摘自《并发实践》一书),现在我已经理解了。 - CaptainHastings
5个回答

9

你误解了——锁是在实例级别获得的。你的示例中只有一个锁,因为当你说:

Widget w = new LoggingWidget

您可以将锁(也称为“监视器”、“互斥量”或“信号量”)视为与JVM中每个对象实例“单独”附加的。如果您在LoggingWidget子类上有另一个synchronized方法,那么您会发现这是真的。无法同时调用此(新)方法和doSomething方法[使用相同对象的不同线程]。对于超类上的另一个synchronized方法也是如此(即它不会受到被覆盖的方法的任何影响)。

5
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()时,你已经锁定了该实例。


1

只有一个实例可以获取锁,即LoggingWidget的实例,从未存在过Widget的实际实例。

当您调用LoggingWidget.doSomething()时,会获得锁定,当您调用super.doSomething()时已经拥有锁定,因此该方法将正常执行。


1

重入机制首先通过获取锁来实现。当一个线程获取了锁时,jvm会知道这个情况。当进入一个与当前正在持有锁的线程同步的代码块时,它们可以在不重新获取锁的情况下继续执行。然后,jvm会在每个可重入操作中增加计数器,并在退出该代码块时逐渐减少,直到计数为零。当计数为零时,锁被释放。


0
如果内部锁不可重入,那么对super.doSomething的调用将永远无法获取锁,因为它被认为已经持有,线程将永久地等待一个它永远无法获取的锁而陷入停顿。在这种情况下,可重入性可以避免死锁。B.Goetz的《Java并发编程实战》。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接