为什么在LinkedHashMap的构造函数中出现间歇性的ConcurrentModificationExceptions?

时间:2019-06-06 22:14:31

标签: java linkedhashmap

下面的静态方法append()的一个版本有时会被各种线程同时调用:

public class CMEDemo {

    private static final Logger LOG = Logger.getLogger(CMEDemo.class.getName());

    private static final String[] SOME_DEMO = {"albino", "mosquito", "libido"};
    private static final Set<String> SET_SOURCE = new LinkedHashSet<>(Arrays.asList(SOME_DEMO));

    public static void append() {
//ConcurrentModificationException is thrown in the constructor for modifiableCopy.
        LinkedHashSet<String> modifiableCopy = new LinkedHashSet<>(getBuiltInFunctions());
        //Exception is not thown here, or later.
        Set<String> doomed = modifiableCopy.stream()
                .filter(f -> f.contains("quit")).collect(Collectors.toSet());
        for (String found : doomed) {
            LOG.log(Level.INFO, found);
        }
    }

    public static Set<String> getBuiltInFunctions() {
        return Collections.unmodifiableSet(SET_SOURCE);
    }

}

通常,所有方法都可以按预期工作,但是有时 LinkedHashSet构造函数会抛出ConcurrentModificationException:

java.util.ConcurrentModificationException
        at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719)
        at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:742)
        at java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1042)
        at java.util.AbstractCollection.addAll(AbstractCollection.java:343)
        at java.util.LinkedHashSet.<init>(LinkedHashSet.java:169)

我的JVM是Java 1.8_212。如果我设置一个测试用例以产生多个线程并让其运行一段时间,则append()最终会引发ConcurrentModificationException。为什么会引发此异常,如何安全地获取LinkedHashSet?

1 个答案:

答案 0 :(得分:0)

其他代码正在修改Set包装而不是复制的SET_SOURCE Collections.unmodifiableSet(..),并且在LinkedHashSet中构造副本期间导致间歇性异常。

所以我将getBuiltInFunctions()替换为

 public static Set<String> getBuiltInFunctions() {
        synchronized (SET_SOURCE) {
            return Collections.unmodifiableSet(new HashSet<>(SET_SOURCE));
        }
    }

    private static Set<String> getFunctionsRW() {
        synchronized (SET_SOURCE ) {
            return SET_SOURCE;
        }
    }