请帮助一下,考虑下面的代码。
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(),或者因为调用子类方法总是会获得对超类的锁定。
干杯
你错了 - 锁是在 例 水平。您的示例中只有一个锁,因为只有当您说:
Widget w = new LoggingWidget
你可以查看锁(也称为 显示器, 互斥 要么 信号灯)作为单独“附加”到每个对象实例 JVM
。
如果你有另一个 synchronized
关于的方法 LoggingWidget
子类,你会看到这是真的。无法调用此(新)方法和 doSomething
方法同时[在同一对象上使用不同的线程]。
这也适用于另一个人 synchronized
超类上的方法(即它不会被覆盖的方法以任何方式影响)。
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()时,您已经锁定了该实例。
只有一个实例可以锁定,实例是 LoggingWidget
,从来没有一个实际的例子 Widget
。
您致电时获得锁定 LoggingWidget.doSomething()
当你打电话时已经锁定了 super.doSomething()
该方法正常执行。
Reentrancy首先获得锁定。当一个线程获得锁时,它在jvm中是已知的。当输入与当前持有锁的线程同步的代码块时,允许它们继续通过而不重新获取锁。然后jvm每次重入动作都会增加一个计数器,当退出该块时直到计数为零时进一步减少。当计数为零时,锁定被释放。
B.Goetz - “实践中的JJava并发”如果内部锁不可重入,对super.doSomething的调用将永远无法获取锁,因为它将被认为已被保留,并且线程将永久停止等待锁定它永远无法获得。在这种情况下,重入可以使我们免于死锁。