2012-05-21 25 views
1

我想在映射初始化時只創建一次鎖定。這是我使用的代碼。這是使靜態方法使單體地圖線程安全的正確和有效的方法

public static Map<String, String> getOrderStatusInstance() { 
    if (orderStatusMap == null) { 
     synchronized (Utility.class) { 
      orderStatusMap = new HashMap<String, String>(); 

      orderStatusMap.put("Key1", "Value1"); 
      orderStatusMap.put("Key2", "Value2"); 
      orderStatusMap.put("Key3", "Value3"); 
      orderStatusMap.put("Key4", "Value4"); 
     } 
    } 

    return orderStatusMap; 
} 
+0

使用靜態初始值設定項。 – birryree

+0

你可以使用'HashTable',它已經是一個線程安全的容器。沒有理由做額外的工作。 –

+2

不,這不是因爲在設置「orderStatusMap」變量之後不知道其他線程在填充之前可能會使用它。此外,兩個線程可能會先通過檢查「if(orderStatusMap == null){...」之前的值將被分配。 – Dmitry

回答

9

不,這是一個壞主意 - 你要返回一個可變的,非線程安全的地圖。你也試圖來實現雙重檢查鎖定,但沒有正確地做 - 它很難得到它比使用靜態初始化。

我會創建一個不可變的映射(使用Guava,對於偏好),理想地作爲類初始化的一部分:(對於超過5鍵/值對,使用ImmutableMap.Builder

private static final ImmutableMap<String, String> STATUS_MAP = ImmutableMap.of(
    "Key1", "Value1", 
    "Key2", "Value2", 
    "Key3", "Value3", 
    "Key4", "Value4"); 

public static ImmutableMap<String, String> getOrderStatusInstance() { 
    return STATUS_MAP; 
} 

你真的需要它是比任何懶惰?

+0

謝謝。我還沒有使用番石榴,看起來很有用! – TastyCode

0

你應該做的是另支票null的​​塊內。

當兩個線程調用你的方法時,他們都會發現orderStatusMap爲空,其中一個會進入​​塊,而另一個會阻塞。但最終它會在同步塊內傳遞並再次初始化地圖。

+1

這還不夠。分配orderStatusMap變量應該是最後一個動作。 – Dmitry

2

幾乎正確...想想一個線程檢查orderStatusMap == null並且爲true的情況。然後調度程序切換到執行相同檢查的另一個線程。兩個線程都不會執行同步塊。

防止這種由另一個檢查在synchronized塊空:

if (orderStatusMap == null) { 
    synchronized (Utility.class) { 
     if (orderStatusMap == null) { 
      Map<String, String> tmp = new HashMap<String, String>(); 

      tmp.put("Key1", "Value1"); 
      tmp.put("Key2", "Value2"); 
      tmp.put("Key3", "Value3"); 
      tmp.put("Key4", "Value4"); 

      orderStatusMap = tmp; 
     } 
    } 
} 
return orderStatusMap; 

是的,這是細做的兩倍。外部檢查仍然有助於提高性能,因爲在創建地圖之後,進入同步塊的昂貴步驟不再需要完成。

請記住,這是創建hashmap的線程安全方法。它不會使地圖線程安全。

PS:如果你喜歡這個問題,你可能還喜歡:How to directly initialize a HashMap (in a literal way)?

+0

'return orderStatusMap;' - 從您的代碼中丟失 - 可能仍會返回部分填充的地圖。看到德米特里對尼普答案的評論。 –

+0

@owlstead:採取點;代碼已更改 – yankee

0

不,它是不正確的

也有沒有懶惰的初始化這裏(我認爲這是過度反正需要 )所以只是這樣做:

private static final Map<String, String> orderStatusMap; 
static{ 
    orderStatusMap = Collections.synchronizedMap(new HashMap<String, String>());//making it thread safe 
    //or use a ConcurrentHashMap 

    orderStatusMap.put("Key1", "Value1"); 
    orderStatusMap.put("Key2", "Value2"); 
    orderStatusMap.put("Key3", "Value3"); 
    orderStatusMap.put("Key4", "Value4"); 
} 

public static Map<String, String> getOrderStatusInstance() { 
    return orderStatusMap; 
}