私は、その文字列値で列挙型オブジェクトを取得utilのを実装したいです。ここに私の実装です。
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
これは、1つのスレッドだけのコード行を実行することを意味します:
System.out.println("aaa:" + stringEnumMap.get(enumClassName));
だから、初期化コードは、1つのスレッドによって実行されなければなりません。
奇妙なことには、いくつかのスレッドの結果がなる、あるnull
コード行を実行した後:
return stringEnumMap.get(enumClassName).get(symbol);
何NullPointerExceptionがありませんので、stringEnumMap.get(enumClassName)
の参照を返す必要がありますinnerMap
。しかし、なぜそれが得られますnull
呼び出した後get(symbol)
のinnerMap
?
ヘルプ、それは一日狂気私を運転してください!
問題は、ラインが原因であります
List<Boolean> resultList = new LinkedList<>();
この実装は同期化されていないことに注意してください。複数のスレッドが同時にリンクリストにアクセスする場合は、リスト構造のスレッドの修正の少なくとも1つは、それが外部で同期をとる必要があります。(構造的修飾は、一つまたは複数の要素を追加または削除する操作であり、単に要素の値を設定する構造変更はない。)これは、典型的にそのようなオブジェクトが存在しない天然list.Ifをカプセル化するいくつかのオブジェクトに同期させることによって達成されます。 、リストはCollections.synchronizedListmethodを使って「ラップ」する必要があります。これは最高のリストへの偶発的な非同期アクセスを防ぐために、作成時に行われます。
List list = Collections.synchronizedList(new LinkedList(...));
LinkedList
スレッドセーフではなく、予期せぬ動作が起こる時にadd
操作。原因となるresultList
サイズより少ないスレッド数よりも、ひいては予想カウントは、結果の数よりも少ないです。
正しい結果を得るには、追加Collections.synchronizedList
提案されているよう。
もし実装が細かいですが、私はあなたが簡単かつ堅牢なソリューションのためのマットTimmermans答えに従うことをお勧めします。