2009-07-09 109 views
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(); 
    } 
} 

我读到,当DoSomething的()在LoggingWidget被调用时,JVM将尝试先获得小窗口上LoggingWidget锁。

我很想知道原因。是因为JVM知道doSomething()调用了super.doSomething(),或者因为调用子类方法总是会获得超类的锁。

干杯

+0

您应该发布一个参考,因为它不是真实的:-) – 2009-07-09 15:45:53

+0

非常感谢您的帮助。我误解了可重入锁定的解释。在阅读您的解释之后,我回到了源代码(实践中并发书的摘录),它现在确实有意义。 – CaptainHastings 2009-07-09 15:57:35

回答

9

你错了 - 在实例水平获得了锁。

Widget w = new LoggingWidget 

您可以查看锁(又称显示器互斥信号灯)为单独“:还有当你说只有一个在您的例子锁,因为只有一个创建的实例附加“到JVM中的每个对象实例。

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

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

1

只有一个实例获取锁,LoggingWidget的实例永远不会有Widget的实际实例。

当您拨打LoggingWidget.doSomething()时会获得该锁定,并且您在调用super.doSomething()时已经锁定该方法正常执行。

+0

感谢尼克,这很有道理。 – CaptainHastings 2009-07-09 15:59:20

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

0

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

0

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