簡體   English   中英

Java並發訪問字段,不使用volatile的技巧

[英]Java concurrent access to field, trick to not use volatile

前言:我知道在大多數情況下,使用易失性字段不會產生任何可衡量的性能損失,但是這個問題更具理論性,並且針對具有極高一致性支持的設計。

我有一個字段,它是構造后填充的List<Something> 為了節省性能,我想將List轉換為只讀Map。 這樣做在任何時候都至少需要一個易失的Map字段,以便使所有線程可見更改。

我正在考慮執行以下操作:

Map map;

public void get(Object key){
    if(map==null){
        Map temp = new Map();
        for(Object value : super.getList()){
            temp.put(value.getKey(),value);
        }
        map = temp;
    }
     return map.get(key);
}

即使它們以串行方式進入get塊,這也可能導致多個線程生成映射。 如果線程在映射的不同相同實例上工作,這將不是什么大問題。 更讓我擔心的是:

是否有可能一個線程將新的臨時映射分配給map字段,然后另一個線程看到該map!=null並因此訪問map字段而不生成新的臨時映射,但令我驚訝的是發現該映射為空,因為put操作尚未推送到某些共享內存區域?

評論答案:

  • 線程僅在只讀后修改臨時映射。
  • 由於某些特殊的JAXB設置,我無法將List轉換為Map,這使得從一開始就沒有Map是可行的。

是否有可能一個線程將新的臨時映射分配給map字段,然后另一個線程看到該map!=null並因此訪問map字段而不生成新的臨時映射,但令我驚訝的是發現該映射為空,因為put操作尚未推送到某些共享內存區域?

是的,這絕對有可能; 例如,一個優化的編譯器實際上可以完全擺脫局部temp變量,並且只要在異常情況下將map恢復為null ,就可以一直使用map字段。

同樣,線程也可能會看到仍然未完全填充的非空,非空map 並且,除非您的Map類經過精心設計以允許同時進行讀寫(或使用synchronized以避免發生問題),否則,如果一個線程調用其get方法而另一個線程調用put ,則您也可能會得到奇怪的行為。

您可以在ctor中創建Map並將其聲明為final嗎? 前提是您不泄漏地圖,以便其他人可以對其進行修改,這足以使您的get()安全地被多個線程共享。

除了前面提到的可見性問題外,原始代碼還有另一個問題,即。 它可以在這里拋出NullPointerException:

return this.map.get(key)

這是違反直覺的,但這是您從錯誤同步的代碼中可以期望的。

防止這種情況的示例代碼:

Map temp;
if ((temp = this.map) == null)
{

    temp = new ImmutableMap(getList());
    this.map = temp;
}
return temp.get(key);

當您真的懷疑其他線程是否可以讀取“半完成”映射(我認為不是,但從不說永不;-)時,可以嘗試使用此方法。

地圖為空或完整

static class MyMap extends HashMap {
   MyMap (List pList) {
    for(Object value : pList){
        put(value.getKey(), value);
    }
   }
}

MyMap map;

public Object get(Object key){
    if(map==null){
        map = new MyMap (super.getList());
    }
    return map.get(key);
 }

還是有人看到一個新引入的問題?

暫無
暫無

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

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