可重入锁定

时间:2009-07-09 15:37:47

标签: java concurrency locking reentrancy

请稍微帮忙,考虑下面的代码。

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(),或者因为调用子类方法总是会获得对超类的锁定。

干杯

5 个答案:

答案 0 :(得分:9)

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

时,只创建了一个实例
Widget w = new LoggingWidget

您可以将锁定(也称为监视器互斥锁信号量)视为单独“附加”到每个对象实例中。 JVM

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

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

答案 1 :(得分: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()时,您已经锁定了该实例。

答案 2 :(得分:1)

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

当您致电LoggingWidget.doSomething()时获得锁定,并且当您致电super.doSomething()时已经锁定该方法正常执行。

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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