1

这是永久“冗长的如果或切换”困境的变体......

考虑一个使用静态方法的多线程应用程序,该方法包含一个长(超过十几个条件)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(),以从类似字符串中检索实际类型名称"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

3 回答 3

5

我找不到任何enum.valueOf(String)不线程安全的原因:

  • 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为空(这里没有可见性问题,因为它是易变的),它将构建映射并将其分配给该变量。由于 volatile 保证,从那个时间点开始,所有其他线程将看到完全构建的映射。
  • 如果另一个线程同时到达该方法并且也观察到一个空值enumConstantDirectory,它将重新创建地图并再次安全地发布它

这里最糟糕的情况是 2 个线程可能正在使用 2 个不同的地图(不同的实例),但它们的内容将是相同的,因此不会导致任何问题。

底线:线程无法看到半构建的映射,因为映射构建是在局部变量上完成的,该变量在填充后分配给 volatile 变量。

于 2012-08-16T12:37:33.637 回答
4

没有理由认为这Enum.valueOf()不是线程安全的。它不会改变任何东西,它只是访问实际enum类中有效的最终状态。

如果此方法是非线程安全的,我认为 javadocs 中会有这样的说法。

于 2012-08-16T12:38:20.183 回答
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 == nullHashMapClassenumType

于 2012-08-16T12:47:27.193 回答