【问题标题】:Why do I get intermittent ConcurrentModificationExceptions in the constructor of LinkedHashMap?为什么我在 LinkedHashMap 的构造函数中会出现间歇性 ConcurrentModificationExceptions?
【发布时间】:2019-06-06 22:14:31
【问题描述】:

下面的静态方法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?

【问题讨论】:

  • 是否有任何代码修改SET_SOURCE(即直接访问它,而不是通过getBuiltInFunctions())?
  • 是的,这就是我制作副本的原因。
  • unmodifiableSet 不会复制。它返回一个 Set,它的修改方法抛出 UnsupportedOperationException,而它的其他方法委托给底层集合。如果该基础集发生更改,则 unmodifiableSet 将反映这些更改 - 如果您正在对其进行迭代,则会导致此异常。
  • 只是为了确保,您知道Collections.unmodifiableSet(..) 不会复制传递的集合,对吧?
  • 不,我不认为这是副本。嗯,这可能解释了它。

标签: java linkedhashmap


【解决方案1】:

其他代码正在修改 Collections.unmodifiableSet(..) 包装而不是复制的 Set SET_SOURCE,这导致在 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;
        }
    }

【讨论】:

  • 好吧,你还需要Collections.unmodifiableSet(..)吗?拥有new HashSet&lt;&gt;(SET_SOURCE) 可以提供您想要的安全性,对吗?
  • 它应该是一个只读副本,并强制客户端制作本地可修改的副本。但是。下一个检查点我会复习,并减少重复。
猜你喜欢
  • 1970-01-01
  • 2020-03-17
  • 1970-01-01
  • 2016-12-08
  • 2013-10-02
  • 2022-07-10
  • 1970-01-01
  • 2013-05-15
  • 2013-09-18
相关资源
最近更新 更多