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() 还是因为调用子类方法也总是会获取超类上的锁。

干杯

4

5 回答 5

9

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

Widget w = new LoggingWidget

您可以将锁(也称为监视器互斥锁信号量)视为单独“附加”到JVM.

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

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

于 2009-07-09T15:40:54.723 回答
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() 时,您已经锁定了该实例。

于 2009-07-09T15:46:56.990 回答
1

只有一个实例可以获取锁,即 的实例LoggingWidget,从来没有 的实际实例Widget

当您调用时获得LoggingWidget.doSomething()锁,并且当您调用super.doSomething()该方法时您已经拥有锁,该方法正常执行。

于 2009-07-09T15:43:09.850 回答
1

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

于 2009-07-09T22:33:49.367 回答
0

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

于 2009-07-10T11:49:56.187 回答