[英]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操作尚未推送到某些共享內存區域?
評論答案:
是否有可能一個線程將新的臨時映射分配給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.