这是“冗长 - 如果或转换”困境的变体......
考虑使用静态方法的多线程应用程序,该方法包含一个长(十几个条件)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
,它是根据values()
数组的副本创建的。
这是线程安全吗?
答案 0 :(得分:4)
我找不到enum.valueOf(String)
不能保证线程安全的任何原因:
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
为空(此处没有可见性问题,因为它是易失性的),它将构造映射并将其分配给该变量。由于易失性保证,从那个时间点开始,所有其他线程都将完全构建地图。enumConstantDirectory
的空值,它将重新创建地图并安全地再次发布最糟糕的情况是2个线程可能使用2个不同的地图(不同的实例),但它们的内容是相同的,因此不会引起任何问题。
底线:线程无法看到一半构造的地图,因为地图构造是在局部变量上完成的,该变量被分配给volatile变量在之后,它已被填充。
答案 1 :(得分:3)
没有理由认为Enum.valueOf()
不是线程安全的。它不会改变任何东西,它只是在实际的enum
类中访问状态,而且实际上是最终的。
如果这个方法 是非线程安全的,我认为javadocs中会有这样的内容。
答案 2 :(得分: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
未在您的应用程序代码中共享,它肯定是安全的