2012-08-16 230 views
1

這是perpertual 「冗長-if或switch」困境的變化......線程安全

使用包含長一個靜態方法(超過一打的考慮多線程應用程序條件)if語句,這相應地將檢查對象的類型,並返回一個值,即像

public static String checkType(Class<?> type) 
{ 
    if (type == A.class) 
    { 
     return aString; 
    } 
    else if (type == B.class) 
    { 
     return bString; 
    } 
    ... 
    else if (type == z.class) 
    { 
     return zString; 
    } 
} 

顯然switch語句是不能直接適用在這裏,所以共同的圖案是有一個enum並調用它valueOf(),即d ø類似

public enum Strings 
{ 
    A(aString), B(bString), ..., Z(zString) 

    private final String value; 

    private Strings(String value) 
    { 
     this.value = value; 
    } 

    public String value() 
    { 
     return this.value; 
    } 
} 

所以,checkType()可以重新寫爲

public static String checkType(Class<?> type) 
{ 
    return Strings.valueOf(getActualTypeName(type.getClass().getName())).value(); 
} 

具有用於在生產代碼和非原始類型一些字符串處理加入null值相應的檢查,則getActualTypeName()方法內,從"class java.lang.Long"等字符串中檢索實際的類型名稱(對於基元,getName()方法返回期望的字符串,例如「long")。

但是,如果valueOf()不是線程安全的,這不會在並行環境下工作。這同樣適用於使用(正常)Map對象,可能這兩個選擇都是相同的模式的變種,因爲enum.valueOf()顯然是基於

Enum.valueOf(Class<T> enumType, String name) 

這就要求

enumType.enumConstantDirectory().get(name); 
Class.java

每次調用enumConstantDirectory()方法時,都會返回一個新的HashMap,它由values()數組的副本創建。

這是線程安全嗎?

回答

4

我找不到任何理由enum.valueOf(String)不會是線程安全的:

  • 字符串是不可變的,所以參數不能突變而valueOf它的工作
  • 的valueOf檢查參數VS 。枚舉常量的名字,他們都是靜態的,最終

是什麼讓你認爲enum.valueOf()不是線程安全的?

編輯

的valueOf電話:

T result = enumType.enumConstantDirectory().get(name); 

其中enumType是你的枚舉類。

enumConstantDirectory()使用此模式:

Map<String, T> enumConstantDirectory() { 
    if (enumConstantDirectory == null) { 
     T[] universe = getEnumConstantsShared(); 
     if (universe == null) 
      throw new IllegalArgumentException(
       getName() + " is not an enum type"); 
     Map<String, T> m = new HashMap<>(2 * universe.length); 
     for (T constant : universe) 
      m.put(((Enum<?>)constant).name(), constant); 
     enumConstantDirectory = m; 
    } 
    return enumConstantDirectory; 
} 

其中enumConstantDirectory是易失性可變:

private volatile transient Map<String, T> enumConstantDirectory = null; 

試想在該方法中同時到達的線程:

  • 如果enumConstantDirectory是null(這裏沒有可見性問題,因爲它是不穩定的),它會c建立地圖並將其分配給該變量。由於不穩定的保證,從那時起,所有其他線程將看到完全構建的地圖。
  • 如果另一個線程的方法到達的同時,也觀察enumConstantDirectory空值,它會重新創建地圖和安全地重新發布

這裏最糟糕的情況是,2個線程可能潛在使用2個不同的地圖(不同的實例),但他們的內容將是相同的,所以它不會導致任何問題。

底線:沒有辦法,一個線程可以看到它是成功的一半構成的地圖,因爲地圖建設中的局部變量,這是分配給volatile變量後已被填充完成。

+0

如果它擴展Enum.valueOf() ,那麼它查找非同步映射。查看Enum.valueOf()方法的來源;它調用enumType.enumConstantDirectory()。get(name)。 – PNS 2012-08-16 12:44:05

+1

@PNS如果你看得更深一些,你會看到'enumConstantDirectory'(變量)是易變的,所以這提供了足夠的保證,以確保映射安全地發佈,並且在構建之後永遠不會改變。最糟糕的情況是地圖不止一次被創建,但是因爲它的創建基於最終狀態,所以它是確定性的,所以所使用的懶惰模式使得它是線程安全的。 – assylias 2012-08-16 12:47:03

+0

你是對的,它似乎是安全的。哪個好! :-) – PNS 2012-08-16 12:54:00

3

沒有理由假設Enum.valueOf()不是線程安全的。它不會改變任何東西,它只是在實際上是最終的enum類中訪問狀態。

如果這種方法非線程安全的,我想在javadoc中會有這樣的說法。

+0

如果它擴展了Enum.valueOf(),那麼它查找非同步映射。查看Enum.valueOf()方法的來源;它調用enumType。enumConstantDirectory()得到(名稱)。 – PNS 2012-08-16 12:44:50

+3

如果它是隻讀的,則在非同步映射中查找是完全安全的。 – stevevls 2012-08-16 12:46:24

+1

@stevevls只有在地圖已被安全發佈。 – assylias 2012-08-16 13:22:00

1

可能是我錯了,但它似乎有一個微妙的問題在這裏:

public static <T extends Enum<T>> T valueOf(Class<T> enumType, 
                 String name) { 
    T result = enumType.enumConstantDirectory().get(name); 
    if (result != null) 
      return result; 
    if (name == null) 
      throw new NullPointerException("Name is null"); 
    throw new IllegalArgumentException(
        "No enum constant " + enumType.getCanonicalName() + "." + name); 
} 

這是調用valueOf代碼。它使用傳遞的enumType創建一個帶有常量的內部HashMap,代碼不是sychronized
似乎有一個微妙的問題在這裏:T result = enumType.enumConstantDirectory().get(name);
enumConstantDirectory()確實爲enumConstantDirectory == null檢查,但不同步,以創建HashMap。也許副作用並不重要(我不知道Class商店有什麼信息),但無論如何它肯定是安全的,只要enumType不在您的應用程序代碼中共享

+0

我不這麼認爲:'enumConstantDirectory()'是使用一個volatile變量來延遲創建的,IMO確保它安全地發佈。 – assylias 2012-08-16 12:49:51

+1

@assylias:我在這裏可能是錯的,但並不能保證只有知名度?沒有同步保證 – Cratylus 2012-08-16 12:51:07

+1

看到我編輯的答案,你是對的。所以地圖可以創建多次,2個線程可以觀察到不同的地圖實例,但所有線程都會觀察到一個有效的地圖(它包含正確的鍵/值)。 – assylias 2012-08-16 13:00:18