首页 > Java > java教程 > 正文

Java并发集合终极性能对比:ConcurrentHashMap vs SynchronizedMap

狼影
发布: 2025-09-03 19:21:01
原创
234人浏览过
ConcurrentHashMap性能优于Collections.synchronizedMap,因其采用CAS与synchronized结合的细粒度锁机制,支持高并发读写;而synchronizedMap使用全局锁,导致高并发下线程阻塞严重。前者在JDK 8中以桶为单位加锁,读操作无锁,写操作仅锁定冲突桶,并支持链表转红黑树优化性能;后者所有方法均同步,吞吐量低。此外,ConcurrentHashMap不支持null键值,提供原子复合操作如putIfAbsent,迭代器弱一致性;synchronizedMap允许null键值,迭代器快速失败,复合操作需外部同步。低并发或需快速失败迭代器时可选synchronizedMap,但多数场景ConcurrentHashMap更优。

java并发集合终极性能对比:concurrenthashmap vs synchronizedmap

在Java并发编程的语境下,

ConcurrentHashMap
登录后复制
Collections.synchronizedMap
登录后复制
都是为了解决多线程环境下
HashMap
登录后复制
的非线程安全问题而生。但要论“终极性能”,我个人会毫不犹豫地把票投给
ConcurrentHashMap
登录后复制
。它在设计之初就考虑了高并发场景下的性能和可伸缩性,而
synchronizedMap
登录后复制
更多的是一种“打补丁”式的解决方案,用全局锁来简单粗暴地实现线程安全,这在高并发下往往会成为性能瓶颈。

解决方案

要深入理解两者的性能差异,我们得从它们实现线程安全的底层机制说起。

Collections.synchronizedMap(new HashMap<>())
登录后复制
的工作原理非常直接:它返回一个
Map
登录后复制
的包装器,这个包装器中的所有公共方法(包括
get
登录后复制
put
登录后复制
remove
登录后复制
size
登录后复制
等)都被
synchronized
登录后复制
关键字修饰,锁住的是这个包装器对象本身。这意味着在任何给定时刻,只有一个线程能够访问这个
Map
登录后复制
的任何一个操作。想象一下,你有一个巨大的图书馆,但只有一个入口,所有读者(线程)无论是借书、还书还是查阅,都必须排队通过这唯一的入口。在高并发场景下,这种全局锁机制会导致严重的竞争,大量线程会因为等待锁而被阻塞,从而极大地降低吞吐量和性能。

ConcurrentHashMap
登录后复制
的设计哲学则完全不同。它采取了一种更精细、更智能的并发控制策略。在JDK 7及之前,
ConcurrentHashMap
登录后复制
采用了分段锁(Segment Locking)的机制,将整个
HashMap
登录后复制
分割成若干个段(Segment),每个段都是一个独立的
HashEntry
登录后复制
数组,并拥有自己的锁。这样,不同的线程就可以同时访问不同的段,进行读写操作,大大减少了锁的粒度。就好比图书馆里有多个独立的阅览室,每个阅览室都有自己的门禁,读者可以同时进入不同的阅览室。

到了JDK 8,

ConcurrentHashMap
登录后复制
的实现进一步优化,彻底放弃了分段锁,转而采用了一种更加细粒度的并发控制:结合了CAS(Compare-And-Swap)操作和
synchronized
登录后复制
关键字。它将
HashMap
登录后复制
的桶(bin)作为基本的锁单元。对于非冲突的操作(比如对不同桶的写入),
ConcurrentHashMap
登录后复制
可以通过CAS操作实现无锁化;而当出现哈希冲突或需要修改特定桶时,它会只对那个桶的头节点进行
synchronized
登录后复制
锁定。更妙的是,在大多数读操作中,
ConcurrentHashMap
登录后复制
甚至不需要加锁,因为它利用了
volatile
登录后复制
关键字和内存屏障来保证数据可见性。这种设计允许大量的并发读操作几乎不受阻塞,并且不同桶之间的写操作也能并行进行,从而在高并发环境下展现出卓越的性能和可伸缩性。

立即学习Java免费学习笔记(深入)”;

所以,从根本上讲,

synchronizedMap
登录后复制
是悲观锁的典型代表,锁的粒度大;而
ConcurrentHashMap
登录后复制
则是乐观锁(CAS)与悲观锁(synchronized)的结合,并且锁的粒度极小,甚至在很多情况下避免了锁。这直接决定了
ConcurrentHashMap
登录后复制
在处理大量并发请求时,能够提供远超
synchronizedMap
登录后复制
的吞吐量和响应速度。

ConcurrentHashMap 到底是如何实现高性能并发的?

要理解

ConcurrentHashMap
登录后复制
的高性能,我们得稍微深入它的内部构造。在JDK 8中,
ConcurrentHashMap
登录后复制
的核心思想是减少锁的竞争,并尽可能地允许并发操作

首先,它内部的哈希表结构由

Node<K,V>[] table
登录后复制
组成,每个
Node
登录后复制
代表一个键值对。当多个线程尝试修改不同的桶时,它们通常不会相互阻塞。这是因为
ConcurrentHashMap
登录后复制
对不同的桶(即
table
登录后复制
数组的不同索引位置)使用独立的锁。具体来说,当一个线程需要修改某个桶中的数据时,它会尝试对该桶的头节点进行
synchronized
登录后复制
锁定。这意味着,如果两个线程修改的是不同的桶,它们可以并行执行,互不影响。

其次,对于读操作

ConcurrentHashMap
登录后复制
几乎是无锁的。它利用
volatile
登录后复制
关键字保证了
Node
登录后复制
数组的可见性,使得一个线程写入的数据,其他线程能够立即看到。在
get()
登录后复制
方法中,它只是简单地遍历哈希桶,查找对应的键,这个过程不需要任何锁。这种设计极大地提升了读操作的性能,因为读操作是并发集合中最频繁的操作之一。

此外,

ConcurrentHashMap
登录后复制
还巧妙地结合了CAS操作来处理一些非冲突的更新。例如,当一个桶为空,第一次插入元素时,它会尝试使用CAS操作来设置
Node
登录后复制
。只有当CAS失败(意味着其他线程抢先一步插入了),它才会退回到使用
synchronized
登录后复制
锁来保证原子性。

最后,当哈希冲突严重导致某个桶的链表过长时(默认阈值是8),

ConcurrentHashMap
登录后复制
会将这个链表转换成红黑树,以保证查找、插入和删除操作的最坏时间复杂度为
O(log n)
登录后复制
,而不是
O(n)
登录后复制
。在对红黑树进行操作时,同样会使用
synchronized
登录后复制
锁来保证线程安全。

这种多层次、混合式的并发控制策略,使得

ConcurrentHashMap
登录后复制
在绝大多数并发场景下都能表现出卓越的性能。它不是简单地给所有操作加锁,而是根据操作类型和冲突程度,智能地选择最合适的并发控制手段,从而实现了高并发下的高吞吐量和低延迟。

标书对比王
标书对比王

标书对比王是一款标书查重工具,支持多份投标文件两两相互比对,重复内容高亮标记,可快速定位重复内容原文所在位置,并可导出比对报告。

标书对比王 58
查看详情 标书对比王

什么时候我们仍然应该考虑使用 SynchronizedMap?

尽管

ConcurrentHashMap
登录后复制
在性能上碾压
synchronizedMap
登录后复制
,但在某些特定场景下,
synchronizedMap
登录后复制
依然有其存在的价值,或者说,使用它并不会带来明显的劣势,甚至可能更简单直观。

在我看来,最主要的情况是低并发环境。如果你的应用程序中,对

Map
登录后复制
并发访问非常少,或者说并发线程数极低,那么
synchronizedMap
登录后复制
的全局锁开销可能根本不会成为性能瓶颈。在这种情况下,
ConcurrentHashMap
登录后复制
内部更复杂的机制(比如更多的内存开销、CAS操作的循环重试等)反而可能带来微小的额外开销。虽然这个开销通常可以忽略不计,但如果你的首要目标是代码的简洁性和易理解性,
synchronizedMap
登录后复制
可能会显得更直接。

其次,当外部已经存在严格的同步机制时。比如,你可能在一个大的

synchronized
登录后复制
块内部操作一个
Map
登录后复制
,或者你的整个业务逻辑本身就已经是单线程处理的,只是偶尔会被其他线程访问。在这种情况下,
synchronizedMap
登录后复制
提供的那层额外同步可能显得多余,但也不会造成伤害,因为它只是提供了一层“保障”。

再者,如果你的业务逻辑对迭代器的一致性有非常严格的要求,并且你能够确保在迭代期间外部不会修改

Map
登录后复制
synchronizedMap
登录后复制
的迭代器是快速失败(fail-fast)的,这意味着如果在迭代过程中
Map
登录后复制
被其他线程修改了,它会立即抛出
ConcurrentModificationException
登录后复制
。这在某些场景下可以帮助你快速发现并发问题。而
ConcurrentHashMap
登录后复制
的迭代器是弱一致性(weakly consistent)的,它可能不会反映迭代器创建之后的所有修改,但也不会抛出异常。这两种行为模式各有优劣,取决于你的具体需求。

最后,遗留系统和兼容性也是一个考虑因素。在一些老旧的项目中,可能已经大量使用了

synchronizedMap
登录后复制
,如果性能不是瓶颈,并且重构到
ConcurrentHashMap
登录后复制
会带来较大的风险和工作量,那么保持现状也未尝不可。毕竟,工程师的时间和项目的稳定性同样重要。

总的来说,选择

synchronizedMap
登录后复制
更多是出于简单性、低并发场景下的足够性以及特定迭代器行为的考量。一旦你预见到有中高并发的可能,或者对性能有哪怕一点点要求,
ConcurrentHashMap
登录后复制
几乎总是更优的选择。

除了性能,两者在功能和行为上还有哪些关键差异?

除了性能上的巨大鸿沟,

ConcurrentHashMap
登录后复制
synchronizedMap
登录后复制
在功能和行为上还存在几个关键且容易被忽视的差异,这些差异有时会影响你的编程决策。

第一个显著区别

null
登录后复制
键和
null
登录后复制
值的支持
ConcurrentHashMap
登录后复制
不允许
null
登录后复制
键或
null
登录后复制
。如果你尝试插入
null
登录后复制
键或
null
登录后复制
值,它会抛出
NullPointerException
登录后复制
。这是出于设计上的考量,
null
登录后复制
在并发环境中可能导致歧义和复杂性,比如无法区分一个键是不存在还是其值就是
null
登录后复制
。而
synchronizedMap
登录后复制
因为内部包装的是
HashMap
登录后复制
,所以它允许一个
null
登录后复制
键和多个
null
登录后复制
,这与
HashMap
登录后复制
的行为保持一致。这个差异在使用时需要特别注意,尤其是在从非并发代码迁移到并发代码时。

第二个是复合操作的原子性

synchronizedMap
登录后复制
的所有单个操作(如
put
登录后复制
get
登录后复制
remove
登录后复制
)都是原子性的,因为它们都通过全局锁保护。但如果你的操作涉及到多个步骤,例如
if (!map.containsKey(key)) map.put(key, value);
登录后复制
这样的“先检查后执行”的复合操作,
synchronizedMap
登录后复制
并不能保证整个复合操作的原子性。在
containsKey
登录后复制
put
登录后复制
之间,其他线程仍然可能修改
Map
登录后复制
。要保证复合操作的原子性,你仍然需要外部的
synchronized
登录后复制
块来包裹整个逻辑。
ConcurrentHashMap
登录后复制
同样如此,它的单个操作是线程安全的,但复合操作也需要额外的同步措施。不过,
ConcurrentHashMap
登录后复制
提供了一些原子性的复合操作方法,比如
putIfAbsent()
登录后复制
compute()
登录后复制
merge()
登录后复制
等,这些方法可以在内部以原子方式执行,从而简化了部分复合操作的实现。

第三个是迭代器的一致性模型。前面提到过,

synchronizedMap
登录后复制
的迭代器是快速失败(fail-fast)的。这意味着如果在迭代过程中,除了迭代器自身的
remove()
登录后复制
方法之外,
Map
登录后复制
被任何其他方式(包括其他线程)结构性地修改了,迭代器会立即抛出
ConcurrentModificationException
登录后复制
。这有助于在开发阶段发现并发修改的问题。而
ConcurrentHashMap
登录后复制
的迭代器是弱一致性(weakly consistent)的。它会反映迭代器创建时
Map
登录后复制
的状态,但可能不会反映迭代器创建之后的所有修改,也不会抛出
ConcurrentModificationException
登录后复制
。这意味着你可能会看到部分更新,或者错过一些更新,但程序不会崩溃。这种设计是为了在并发环境中提供更高的可用性和吞吐量,但代价是迭代结果的严格一致性。

理解这些非性能层面的差异,对于在特定场景下做出正确的选择至关重要。你不仅仅要考虑“快不快”,还要考虑“好不好用”、“会不会出问题”以及“我的数据模型是否允许

null
登录后复制
”。

以上就是Java并发集合终极性能对比:ConcurrentHashMap vs SynchronizedMap的详细内容,更多请关注php中文网其它相关文章!

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号