请稍微帮忙,考虑下面的代码。
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(),或者因为调用子类方法总是会获得对超类的锁定。
干杯
答案 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的调用将永远无法获取锁,因为它将被认为已被保留,并且线程将永久停止等待一个它永远无法获得的锁。在这样的情况下,重入可以使我们免于死锁。