2012-10-31 51 views
74

我讀了關於ConcurrentHashMap的JDK源代碼。爲什麼JDK源代碼需要`volatile`實例的最終副本

但下面的代碼搞糊塗了:

public boolean isEmpty() { 
    final Segment<K,V>[] segments = this.segments; 
    ... 
} 

我的問題是:

「this.segments」 聲明:

final Segment<K,V>[] segments; 

所以,在這裏,在的開始方法,聲明一個相同的類型引用,指向相同的內存。

作者爲什麼這樣寫呢?爲什麼他們不直接使用this.segments?有什麼理由嗎?

回答

94

這是一個典型的無鎖代碼涉及volatile變量的習慣用法。在第一行中,您只需閱讀volatile一次,然後使用它。同時,另一個線程可以更新volatile,但您只對最初閱讀的值感興趣。

此外,即使所討論的成員變量不是易失性的,也不是最終的,這個習慣用法與CPU緩存相關,因爲從堆棧位置讀取比從隨機堆位置讀取更容易緩存。局部變量最終會綁定到CPU寄存器的機會也更大。

對於後一種情況,實際上存在一些爭議,因爲JIT編譯器通常會處理這些問題,但Doug Lea是一般原則中堅持使用它的人之一。

+0

所以如果有人改變'this.segments'的內容,你不會在你的'segments'中看到這種變化嗎? – brimborium

+0

當然你會看到它。但是如果某人給「分段」指定了別的東西,那麼顯然你會與此分離。 –

+3

啊,是的,當然。 Missunderstood你的答案......;) – brimborium

19

我想這是考慮到性能,所以我們只需要檢索一次字段值。

可以指單成語從有效的Java由約書亞布洛赫

他單是在這裏:

private volatile FieldType field; 
FieldType getField() { 
    FieldType result = field; 
    if (result == null) { 
    synchronized(this) { 
     result = field; 
     if (result == null) 
     field = result = computeFieldValue(); 
    } 
    } 
    return result; 
} 

,他寫道:

此代碼,可能會出現一個位令人費解的。特別是,對局部變量結果的需求可能不清楚。該變量對 的作用是確保在已經初始化 的常見情況下該字段只讀一次。 儘管並非嚴格必要,但這可能會提高 的性能,並且由於應用於低層次併發編程的標準而更加優雅。在我的機器上,上述方法比沒有本地變量的明顯版本快大約25 %。

+0

+1引用來源。 –

4

它可能會減少字節碼的大小 - 訪問本地變量的字節碼比訪問實例變量要短。

運行時優化開銷也可能會降低。

但這些都不重要。這更多關於代碼風格。無論如何,如果您對實例變量感到滿意。道格利亞可能感覺處理局部變量更舒服。

相關問題