【发布时间】:2020-02-29 13:52:33
【问题描述】:
我想实现一个通过字符串值获取枚举对象的工具。这是我的实现。
IStringEnum.java
public interface IStringEnum {
String getValue();
}
StringEnumUtil.java
public class StringEnumUtil {
private volatile static Map<String, Map<String, Enum>> stringEnumMap = new HashMap<>();
private StringEnumUtil() {}
public static <T extends Enum<T>> Enum fromString(Class<T> enumClass, String symbol) {
final String enumClassName = enumClass.getName();
if (!stringEnumMap.containsKey(enumClassName)) {
synchronized (enumClass) {
if (!stringEnumMap.containsKey(enumClassName)) {
System.out.println("aaa:" + stringEnumMap.get(enumClassName));
Map<String, Enum> innerMap = new HashMap<>();
EnumSet<T> set = EnumSet.allOf(enumClass);
for (Enum e: set) {
if (e instanceof IStringEnum) {
innerMap.put(((IStringEnum) e).getValue(), e);
}
}
stringEnumMap.put(enumClassName, innerMap);
}
}
}
return stringEnumMap.get(enumClassName).get(symbol);
}
}
我写了一个单元测试来测试它是否在多线程情况下工作。
StringEnumUtilTest.java
public class StringEnumUtilTest {
enum TestEnum implements IStringEnum {
ONE("one");
TestEnum(String value) {
this.value = value;
}
@Override
public String getValue() {
return this.value;
}
private String value;
}
@Test
public void testFromStringMultiThreadShouldOk() {
final int numThread = 100;
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch doneLatch = new CountDownLatch(numThread);
List<Boolean> resultList = new LinkedList<>();
for (int i = 0; i < numThread; ++i) {
new Thread(() -> {
try {
startLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
resultList.add(StringEnumUtil.fromString(TestEnum.class, "one") != null);
doneLatch.countDown();
}).start();
}
startLatch.countDown();
try {
doneLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
assertEquals(numThread, resultList.stream().filter(item -> item.booleanValue()).count());
}
}
测试结果是:
aaa:null
java.lang.AssertionError:
Expected :100
Actual :98
表示只有一个线程执行这行代码:
System.out.println("aaa:" + stringEnumMap.get(enumClassName));
所以初始化代码应该只由一个线程执行。
奇怪的是,执行这行代码后,某个线程的结果会是null:
return stringEnumMap.get(enumClassName).get(symbol);
由于没有 NullPointerException,stringEnumMap.get(enumClassName) 必须返回 innerMap 的引用。但是为什么调用innerMap的get(symbol)后会得到null呢?
请帮忙,这让我整天发疯!
【问题讨论】:
标签: java multithreading java.util.concurrent concurrenthashmap