2012-08-16 231 views
1

这是perpertual “冗长-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(),即d ø类似

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

我找不到任何理由enum.valueOf(String)不会是线程安全的:

  • 字符串是不可变的,所以参数不能突变而valueOf它的工作
  • 的valueOf检查参数VS 。枚举常量的名字,他们都是静态的,最终

是什么让你认为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(这里没有可见性问题,因为它是不稳定的),它会c建立地图并将其分配给该变量。由于不稳定的保证,从那时起,所有其他线程将看到完全构建的地图。
  • 如果另一个线程的方法到达的同时,也观察enumConstantDirectory空值,它会重新创建地图和安全地重新发布

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

底线:没有办法,一个线程可以看到它是成功的一半构成的地图,因为地图建设中的局部变量,这是分配给volatile变量后已被填充完成。

+0

如果它扩展Enum.valueOf() ,那么它查找非同步映射。查看Enum.valueOf()方法的来源;它调用enumType.enumConstantDirectory()。get(name)。 – PNS 2012-08-16 12:44:05

+1

@PNS如果你看得更深一些,你会看到'enumConstantDirectory'(变量)是易变的,所以这提供了足够的保证,以确保映射安全地发布,并且在构建之后永远不会改变。最糟糕的情况是地图不止一次被创建,但是因为它的创建基于最终状态,所以它是确定性的,所以所使用的懒惰模式使得它是线程安全的。 – assylias 2012-08-16 12:47:03

+0

你是对的,它似乎是安全的。哪个好! :-) – PNS 2012-08-16 12:54:00

3

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

如果这种方法非线程安全的,我想在javadoc中会有这样的说法。

+0

如果它扩展了Enum.valueOf(),那么它查找非同步映射。查看Enum.valueOf()方法的来源;它调用enumType。enumConstantDirectory()得到(名称)。 – PNS 2012-08-16 12:44:50

+3

如果它是只读的,则在非同步映射中查找是完全安全的。 – stevevls 2012-08-16 12:46:24

+1

@stevevls只有在地图已被安全发布。 – assylias 2012-08-16 13:22:00

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不在您的应用程序代码中共享

+0

我不这么认为:'enumConstantDirectory()'是使用一个volatile变量来延迟创建的,IMO确保它安全地发布。 – assylias 2012-08-16 12:49:51

+1

@assylias:我在这里可能是错的,但并不能保证只有知名度?没有同步保证 – Cratylus 2012-08-16 12:51:07

+1

看到我编辑的答案,你是对的。所以地图可以创建多次,2个线程可以观察到不同的地图实例,但所有线程都会观察到一个有效的地图(它包含正确的键/值)。 – assylias 2012-08-16 13:00:18