枚举valueOf()的线程安全 [英] Thread safety of enum valueOf()

查看:275
本文介绍了枚举valueOf()的线程安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是perpertual longy-if或switch困境的变体......



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

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

显然,switch语句不能直接应用于此,所以一个常见的模式是拥有枚举并调用其 valueOf(),即执行类似

$的操作b
$ b

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

private final String值;

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

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

所以, checkType() 可以重写为

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

并适当检查 null getActualTypeName()方法内的生产代码中添加的值和非原始类型的一些String处理,以从<$ c $等字符串中检索实际的类型名称c>class java.lang.Long(对于基元, getName()方法返回预期的字符串,例如 long)。



但是,如果 valueOf()不是线程-safe,这在并发环境中不起作用。这同样适用于使用(普通) Map 对象,可能这两个替代方案是相同模式的变体,因为 enum.valueOf()显然基于

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

调用

  enumType.enumConstantDirectory()得到(名称)。 

Class.java 类中的

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



这是线程安全的吗?

解决方案

我找不到任何理由 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)
抛出新的IllegalArgumentException(
getName()+不是枚举类型);
Map< String,T> m = new HashMap<>(2 * universe.length);
for(T constant:universe)
m.put(((Enum<?>)constant).name(),constant);
enumConstantDirectory = m;
}
返回enumConstantDirectory;
}

其中 enumConstantDirectory 是一个volatile变量:

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

想象一个线程在该方法中同时到达:




  • 如果 enumConstantDirectory 为null(此处没有可见性问题,因为它是易失性的),它将构造地图并将其分配给变量。由于volatile的保证,从那个时间点起所有其他线程都会看到地图完全构造。

  • 如果另一个线程同时到达方法并且也观察到null值 enumConstantDirectory ,它将重新创建地图并安全地再次发布



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



底线:线程无法看到一半构造的地图,因为地图构造是在局部变量上完成的,该变量分配给后的 已填充。


This is a variation of the perpertual "lengthy-if or switch" dilemma...

Consider a multithreaded application using a static method that contains a long (over a dozen conditions) if statement, which checks the type of an object and returns a value accordingly, i.e. something like

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;
    }
}

Obviously a switch statement is not directly applicable here, so a common pattern is to have an enum and call its valueOf(), i.e. do something like

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;
    }
}

So, checkType() could be re-written as

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

with appropriate checks for null values added in production code and some String processing for non-primitive types, inside the getActualTypeName() method, to retrieve the actual type name from strings like "class java.lang.Long" (for primitives, the getName() method returns the expected string, e.g. "long").

However, if valueOf() is not thread-safe, this will not work in a concurrent environment. The same applies to using a (normal) Map object and probably these two alternatives are variants of the same pattern, since enum.valueOf() is apparently based on

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

which calls

enumType.enumConstantDirectory().get(name);

in the Class.java class.

The enumConstantDirectory() method, every time invoked, returns a new HashMap, created from a copy of the values() array.

Would that be thread safe?

解决方案

I can't find any reasons why enum.valueOf(String) would not be thread safe:

  • strings are immutable, so the argument can't be mutated while valueOf does its job
  • valueOf checks the argument vs. the names of the enum constants and they are all static and final

What makes you think that enum.valueOf() is not thread safe?

EDIT

valueOf calls:

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

where enumType is your enum class.

enumConstantDirectory() uses this pattern:

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;
}

where enumConstantDirectory is a volatile variable:

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

Imagine a thread arriving concurrently in that method:

  • if enumConstantDirectory is null (there is no visibility issue here because it is volatile), it will construct the map and assign it to that variable. Because of the volatile guarantees, all other threads, from that point in time, will see the map fully constructed.
  • if another thread arrives in the method at the same time and also observe a null value of enumConstantDirectory, it will recreate the map and safely publish it again

The worst case scenario here is that 2 threads could potentially be using 2 different maps (different instances) but their content will be the same so it would not cause any issues.

Bottom line: there is no way that a thread could see a map which is half constructed, because the map construction is done on a local variable, which is assigned to the volatile variable after it has been populated.

这篇关于枚举valueOf()的线程安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆