【发布时间】:2016-03-16 10:34:41
【问题描述】:
下面的代码 sn-p 从并行流的 forEach 块更新非线程安全映射(itemsById 不是线程安全的)。
// Update stuff in `itemsById` by iterating over all stuff in newItemsById:
newItemsById.entrySet()
.parallelStream()
.unordered()
.filter(...)
.forEach(entry -> {
itemsById.put(entry.getKey(), entry.getValue()); <-- look
});
对我来说,这看起来不是线程安全的,因为并行流会同时在多个线程中调用forEach 块,从而在多个线程中同时调用itemsById.put(..),并且@ 987654326@ 不是线程安全的。 (但是,我认为使用 ConcurrentMap 代码是安全的)
我写信给同事:“请注意,当您插入新数据时,映射可能会分配新内存。这可能不是线程安全的,因为集合不是线程安全的。--无论是否写入不同的来自多个线程的键,是线程安全的,是依赖于实现的,我想。我不会选择依赖它。”
然而,他说上面的代码是线程安全的。 -- 是吗?
((请注意:我不认为这个问题过于本地化。实际上现在使用 Java 8,我认为相当多的人会做类似的事情:parallelStream()...foreach(...),然后可能会很好地了解线程安全问题,因为很多人))
【问题讨论】:
-
如果有人说“这段代码是线程安全的”但没有解释为什么,你不应该相信他。即使某些东西本质上是安全的,例如因为它没有可变数据,这是你可以告诉别人的解释。关于任意
Map实现,您不需要在并发更新的情况下内存分配失败,对象结构的简单重新链接可能会导致任意错误。即使像增加int保持地图大小这样简单的操作也不是原子的,并且在并发执行时可能导致更新丢失,从而产生不一致的数据结构。
标签: java thread-safety java-stream