使用ComputeIfAbsent方法时抛出ConcurrentModificationException异常

3
我有一个HashMap,其中维护了不同类型的读取器到它们各自的Java类实现的映射。我有一个支持32种读取器的多线程Java服务器。
您可以假设每30秒钟,针对每种读取器,1000个对象内部调用getReader()。
当我将刷新时间减少到20秒时,它开始偶尔为某些读取器抛出ConcurrentModificationException。这是否可以预料?
缩短时间周期会有什么不同?
class Mapper {
    Map<String, Reader> READER = new HashMap<>();

    public static Reader getReader(type) {
        Reader reader = READER.computeIfAbsent(type, k -> new ReaderImpl());
    }
}


使用一个同步的映射表。ConcurrentHashMap - undefined
@michalk 是的,我知道 ConcurrentHashMap,但这仍然不能解释为什么当刷新时间为30秒时它能正常工作。 - undefined
3
可能只是巧合。如果你减少时间,那么Threads会更有可能尝试访问Map,并破坏它,因为你没有使用同步集合。另外,如果你运行你的应用程序足够多次,你也有可能在30秒刷新时间内遇到这种情况。 - undefined
1
附注:READER不应该大写,因为它不是一个常量。 - undefined
永远不要依赖时间间隔来证明程序的正确性。 - undefined
2个回答

3
如果您的地图可以被多个线程访问,您应该使用ConcurrentHashMap

每当我将刷新时间减少到20秒时,它开始不定期地为某些读者抛出ConcurrentModificationException。这是正常的吗?

这可能只是巧合。如果您缩短时间,就会更有可能导致线程尝试访问地图并破坏它,因为您没有使用同步集合。还有可能,如果您运行足够多次应用程序,您也会在30秒刷新时间内获得它。调试多线程应用程序很难,因为您可能认为您的应用程序运行良好-但最终证明,对于1000个中的1个,您处理了多线程方式错误。
正如Andy Turner正确指出的那样-您很幸运出现了ConcurrentModificationException。如果您没有收到任何异常,则并不意味着问题不存在。如果您在生产环境中运行应用程序时收到此类错误,情况将更糟。

3
我认为OP很幸运它抛出了异常。当没有抛出异常时,事情实际上可能并没有正常工作,只是因为问题没有被检测到而默默地做错了事情。 - undefined
是的,我完全同意你的观点。这样的错误可能不会出现,但是一旦出现,你就知道你处理多线程的方式是错误的。如果错误没有出现,并不意味着没有错误 :) - undefined
@AndyTurner 这是否意味着我在这里也可能会遇到错误,并且我的某个线程可能会悄悄终止?这是我在这里的另一个观察。 - undefined

2
这是在HashMapJavadoc中所述(强调为他们的内容):

请注意,此实现未同步。如果多个线程同时访问哈希映射,并且其中至少一个线程在结构上修改了映射,则必须在外部进行同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅更改实例已包含的键的关联值不是结构修改。)

就是否预期会出现ConcurrentModificiationException而言,我不知道。应该预期的是行为是未定义的

如果你很幸运,会发生异常。异常是好的:它们告诉你正在做错事情。如果你不太幸运,它会悄悄地失败。谁知道在你特定的情况下会发生什么?也许它会正常工作,也许不会。

如果您希望代码表现正确,请按照文档使用类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接