如果有ConcurrentHashMap,为什么需要synchronizedMap()呢?

4
我读到了一篇关于线程安全映射的文章,有一个问题。 Collections.synchronizedMap() 通过在每个方法上添加 synchronized 块来代理底层映射。另一方面,ConcurrentHashMap 在读/写操作时不锁定整个映射。这意味着多线程系统中的所有操作都更快。
那么,现在使用 synchronizedMap() 的好处是什么?我只看到了以下几点:
  1. 它自Java 1.2以来就可用(而 ConcurrentHashMap 是从 Java 1.5 开始提供的)
  2. 可以存储可空值(如果底层映射可以实现)
是否还有其他情况下使用 synchronizedMap() 更好呢?

1
如果您想同步的映射不是“HashMap”呢? - RobCo
CHM允许更大的并行化和可扩展性,但增加的复杂性会消耗额外的内存和性能,因此在低争用环境中可能不值得。此外,synchronizedMap()可以包装各种映射类型,如EnumMapLinkedHashMap - shmosel
4个回答

2

使用 Collections.synchronizedMap(map)ConcurrentHashMap 都有优缺点。

synchronizedMap 适用于需要数据一致性的场景。每个访问线程都会得到一个更新的 map 视图,这是通过阻塞 map 实现的,但也会降低其性能。

ConcurrentHashMap 适用于需要频繁修改 map 的场景。由于它对 map 进行分段/分区处理,因此多个线程可以同时进行操作。但是存在一个问题,即某个访问线程可能无法获得 map 的更新视图。另一个优点是它是 fail-safe 的。 ConcurrentHashMap 不允许 null 键或值。

如果性能是首要问题而非数据一致性,请使用 ConcurrentHashMap


这不太对。ConcurrentHashMap将表分成了多个段。默认为16,但您可以传递更高的值。这意味着您有1/16的机会访问的条目将被另一个进程锁定。因此,您将始终拥有地图的最新图片,但性能惩罚的可能性较小,而使用synchronizedMap则会锁定整个内容。 - robert
@robert,你所说的关于ConcurrentHashMap分段的内容是正确的,我并不是在说当访问它时ConcurrentHashMap不会提供更新后的地图视图。但是在高并发环境下,ConcurrentHashMap有可能无法返回更新后的地图视图。 - Geek
我想这取决于你如何定义“更新” - 读取不会反映任何待处理的写入...但是尚未进行的写入而不是读取返回过时值。我认为你的答案只需要一些澄清就可以了。 - robert

1
不是很确切。我能想到的唯一另一种情况是轻松地使自定义地图实现线程安全。

0

来自ConcurrentHashMap文档:

"...没有任何支持以一种可以防止所有访问的方式锁定整个表格"

当遍历ConcurrentHashMap元素时,您可能会看到其他线程同时进行的更新。如果要防止此类更新,则可以改用Collections.synchronizedMap()并将迭代逻辑放在synchronized(map)块中。


0

我可以通过互斥锁和信号量的类比来翻译这个内容。

就像任何互斥锁一样,synchronizedMap 只允许一个线程访问后备映射。这确保了没有其他线程能够读取/写入映射中的条目。

对于 ConcurrentHashMap,就像信号量一样,我们决定并发级别,即有多少线程可以同时查看您的 Map 条目。

关于何时使用互斥锁和何时使用信号量的研究可以帮助您进一步澄清疑虑。


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