簡體   English   中英

使用雙重檢查成語重置延遲加載的字段

[英]Resetting a field lazy-loaded with the double-check idiom

考慮“對實例字段的延遲初始化進行雙重檢查”:

  //在Bloch的采訪中復制了Effective Java中的第71項。\n private volatile FieldType字段;\n FieldType getField(){\n     FieldType結果=字段;\n     if(result == null){//首先檢查(無鎖定)\n         synchronized(this){\n             result = field;\n             if(result == null)//第二次檢查(帶鎖定)\n                 field = result = computeFieldValue();\n         }\n     }\n      返回結果;\n } 

我希望能夠以安全的方式重置字段(強制它再次從數據庫加載,在我的情況下)。 我假設我們可以通過重置方法來做到這一點:

\n void reset(){\n    field = null;\n } 

這是重置場地的標准方法嗎? 安全嗎? 任何陷阱? 我問,因為布洛赫發出了關於雙重檢查懶惰加載的以下警告:“成語非常快,但也很復雜和細膩,所以不要試圖以任何方式修改它。只需復制和粘貼 - 通常這不是一個好主意,但在這里是合適的。“

在此先感謝喜馬拉雅山脈的Playa。

是的,這是線程安全的。

synchronized塊用於防止多個線程不必要地調用computeFieldValue() 由於field是易失性的,因此resetgetField中的訪問都是有序的。

如果第一次檢查是非null,則getField完成; 返回result

否則,獲取鎖,排除可能將該字段設置為非null的任何其他線程,但允許任何線程將field設置為null。 如果任何線程將field設置為null,則不應該更改任何內容; 這是使線程進入同步塊的條件。 如果另一個線程在當前線程檢查后已經獲取了鎖,並將該字段設置為非空值,則第二個檢查將檢測到該值。

我認為這應該是安全的,但這只是因為你將字段存儲在局部變量中。 完成此操作后,即使另一個線程正在重置字段的值,也無法將局部變量引用神奇地更改為null。

我認為reset()方法不正確。 如果你閱讀第71項,你會發現:

此代碼可能看起來有點復雜。 特別是,對局部變量結果的需求可能不清楚。 這個變量的作用是確保該字段在已經初始化的常見情況下只讀一次。

延遲初始化不會假設該字段可能已更改。 如果這些運算符之間的字段將設置為null:

FieldType result = field;
if (result == null) { // First check (no locking)

getField()提供不正確的結果。

只要reset方法是上面列出的reset()方法,這似乎就可以工作。 但是,如果reset()方法實例化一個新的Object(如下所示),你最終是否可能返回與你想要的不同的東西?

void reset() {
    field = new FieldType();
}

我想這完全取決於線程安全的確切含義。

最終可能會遇到第二個實例后使用第一個實例的情況。 這可能沒問題,也可能沒有。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM