首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >枚举valueOf()的线程安全

枚举valueOf()的线程安全
EN

Stack Overflow用户
提问于 2012-08-16 12:33:32
回答 3查看 2.3K关注 0票数 1

这是一个永久的变化“冗长-如果或切换”的困境.

考虑使用静态方法的多线程应用程序,该方法包含一个长(十几个条件) if语句,该语句检查对象的类型并相应地返回值,例如

代码语言:javascript
代码运行次数:0
运行
复制
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;
    }
}

显然,开关语句在这里并不直接适用,因此常见的模式是拥有一个enum并调用它的valueOf(),即执行以下操作

代码语言:javascript
代码运行次数:0
运行
复制
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()可以重写为

代码语言:javascript
代码运行次数:0
运行
复制
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()显然是基于

代码语言:javascript
代码运行次数:0
运行
复制
Enum.valueOf(Class<T> enumType, String name)

打电话

代码语言:javascript
代码运行次数:0
运行
复制
enumType.enumConstantDirectory().get(name);

Class.java类中。

每次调用enumConstantDirectory()方法时,都会返回从values()数组的副本创建的新HashMap

那是线安全吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-08-16 12:37:33

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

  • 字符串是不可变的,因此在valueOf完成其工作时不能对参数进行变异。
  • valueOf检查参数与枚举常量的名称,它们都是静态的和最终的

为什么您认为enum.valueOf()不是线程安全的呢?

编辑

valueOf调用:

代码语言:javascript
代码运行次数:0
运行
复制
T result = enumType.enumConstantDirectory().get(name);

其中enumType是您的枚举类。

enumConstantDirectory()使用此模式:

代码语言:javascript
代码运行次数:0
运行
复制
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是一个可变变量:

代码语言:javascript
代码运行次数:0
运行
复制
private volatile transient Map<String, T> enumConstantDirectory = null;

假设一个线程以该方法并发到达:

  • 如果enumConstantDirectory为null (这里不存在可见性问题,因为它是易失性的),它将构造映射并将其赋值给该变量。由于不稳定的保证,所有其他线程,从那时起,将看到映射完全构建。
  • 如果另一个线程同时到达该方法并观察到enumConstantDirectory的空值,它将重新创建映射并再次安全地发布它。

最坏的情况是,两个线程可能使用两个不同的映射(不同的实例),但是它们的内容是相同的,因此不会引起任何问题。

底线:线程不可能看到一半构造的映射,因为映射构造是在局部变量上完成的,该局部变量在填充后分配给易失性变量

票数 5
EN

Stack Overflow用户

发布于 2012-08-16 12:38:20

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

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

票数 4
EN

Stack Overflow用户

发布于 2012-08-16 12:47:27

也许我错了,但这里似乎有一个微妙的问题:

代码语言:javascript
代码运行次数:0
运行
复制
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,它肯定是安全的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11987224

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档