2009-07-09 50 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的調用將永遠無法獲得鎖,因爲它會被認爲已被佔用,並且該線程將永久停止等待對於它永遠無法獲得的鎖定。重入不讓我們在這種情況下陷入僵局。