簡體   English   中英

枚舉valueOf()的線程安全性

[英]Thread safety of enum valueOf()

這是普通的“冗長的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() ,即執行類似的操作

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()方法內對非原始類型進行一些String處理,以從諸如"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 ,該HashMap是從values()數組的副本創建的。

那是線程安全的嗎?

我找不到為什么enum.valueOf(String)不是線程安全的任何原因:

  • 字符串是不可變的,因此在valueOf執行其工作時不能對參數進行valueOf
  • valueOf檢查參數與枚舉常量的名稱,它們都是靜態的和最終的

是什么讓您認為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(此處不存在可見性問題,因為它是易失性的),它將構造映射並將其分配給該變量。 由於存在可變保證,因此從該時間點開始,所有其他線程將看到映射已完全構建。
  • 如果另一個線程同時到達該方法並且還觀察到enumConstantDirectory的空值,則它將重新創建地圖並再次安全地發布它

這里最壞的情況是2個線程可能使用2個不同的映射(不同的實例),但是它們的內容將相同,因此不會引起任何問題。

底線 :線程無法看到一半構造的映射,因為映射構造是在局部變量上完成的,該局部變量在填充分配給volatile變量。

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

如果此方法是非線程安全的,我認為會有什么東西在Javadoc中這樣說。

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

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);
為了創建HashMapenumConstantDirectory()enumConstantDirectory == null了檢查, enumConstantDirectory == null同步。 也許副作用並不重要(我不知道Class存儲什么信息),但是在任何情況下,只要在應用程序代碼中未共享enumType它肯定是安全的

暫無
暫無

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

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