Java中SynchronizedMap与ConcurrentHashMap的对比

本文对比分析了ConcurrentHashMap与SynchronizedMap两种线程安全的Map实现。ConcurrentHashMap采用锁分段技术提高并发性能,而SynchronizedMap通过synchronized关键字实现线程安全。文章详细解释了ConcurrentHashMap的get和put方法实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何使用

概述

  • ConcurrentHashMap

    线程安全;
    其将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都有锁存在,那么在插入元素的时候就要先找到应该插入到哪一个片段segment,然后再在这个片段上面进行插入,而且这里还需要获取segment锁(即锁分段技术);
    ConcurrentHashMap让锁的粒度更精细一些,并发性能更好

  • SynchronizedMap:

    线程安全;
    通过synchronized关键字进行同步控制;
    所有单个的操作都是线程安全的,但是多个操作组成的操作序列却可能导致数据争用,因为在操作序列中控制流取决于前面操作的结果。这也被称作是:有条件的线程安全性
    效率低

两者源码对比

  哈希表声明:通过下面两图对比可以发现,两者的区别在于volatile关键字


  • ConcurrentHashMap:

  • SynchronizedMap:

  put()方法:


  • ConcurrentHashMap:

  • SynchronizedMap:

  get()方法:


  • ConcurrentHashMap:

  • SynchronizedMap:

ConcurrentHashMap详解

  1. 与HashTable容器对比:HashTable容器在竞争激烈的并发环境下表现出效率底下的原因是所有访问HashTable的线程都必须竞争同一把锁(其实现是在对应的方法上添加了synchronized关键字进行修饰),那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
  2. get()方法:其高效之处在于整个get过程不需要加锁,除非读到的值是空的才会加锁重读。get方法里将要使用的共享变量都定义成volatile,如用于统计当前Segement大小的count字段和用于存储值的HashEntry的value。定义成volatile的变量,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写(有一种情况可以被线程写,就是写入的值不依赖于原值),在get操作里只需要读不需要写共享变量count和value,所以可以不用加锁。
  3. put()方法:首先定位到Segment,然后在Segment里进行插入操作。插入操作需要经历两个步骤,第一步判断是否需要对Segment里的HashEntry数组进行扩容,第二步定位添加元素的位置然后放在HashEntry数组里。
  4. 具体实现:ConcurrentHashMap的实现中使用了一个包含16个锁的数组,每个锁保护所有散列桶的1/16,其中第N个散列桶由第(N mod 16)个锁来保护。假设散列函数具有合理的分布性,并且关键字能够实现均匀分布,那么这大约能把对于锁的请求减少到原来的1/16。正是这项技术使得其能够支持多达16个并发的写入器。
  5. 缺点:当ConcurrentHashMap需要扩展映射范围,以及重新计算键值得散列值要分布到更大的桶集合中时,就需要获取分段锁集合中所有的锁(要获取内置锁的一个集合,采用的唯一方式是递归)。
<think>嗯,用户之前已经问过SynchronizedMapConcurrentHashMap的区别,现在他们想更深入理解两者的使用场景和设计目的差异,特别是为什么还需要SynchronizedMap。我需要结合之前的回答和用户提供的引用内容来组织答案。 首先,回顾之前的回答,重点提到了锁机制、并发性能、迭代器行为和适用场景。用户现在可能希望更明确两者的设计哲学和适用情况,以及SynchronizedMap存在的必要性。需要确保回答结构清晰,分点说明。 用户提供的引用中提到,SynchronizedMap通过同步整个Map来保证一致性,但性能较低,而ConcurrentHashMap使用分段锁提升并发性能,但可能有更高的风险。引用还提到ConcurrentHashMap的锁粒度更小,推荐使用。不过用户现在的问题是,既然ConcurrentHashMap更好,为什么还需要SynchronizedMap?这可能涉及到兼容性、简单场景的需求或者某些特殊用例。 接下来,需要整理两者的设计目的:SynchronizedMap是为了简单的线程安全封装,而ConcurrentHashMap是为了高并发设计的。使用场景方面,SynchronizedMap适合低并发或旧系统维护,ConcurrentHashMap适合高并发环境。同时,需要解释为什么SynchronizedMap仍然存在,比如兼容旧代码、简单使用、或者不需要高并发的场景。 还需要注意用户可能存在的深层需求,比如在实际项目中如何选择,或者是否有其他替代方案。可能需要提到虽然ConcurrentHashMap在大多数情况下更优,但某些情况下SynchronizedMap可能更合适,比如当需要其他同步代码块配合,或者应用在并发不高的场景中。 另外,引用中的信息需要正确整合,比如引用[2]中的代码示例说明SynchronizedMap的全局锁,引用[3]提到的锁粒度差异。同时,要确保引用标记正确,例如关于性能对比和锁机制的部分。 需要避免技术错误,比如Java版本的区别,Java 8之后ConcurrentHashMap的实现变化,是否影响使用场景。同时,回答要符合中文习惯,使用清晰的结构,比如分点说明设计目的、使用场景,以及SynchronizedMap存在的理由。 最后,生成的相关问题要围绕使用场景、设计考虑和实际应用,帮助用户进一步理解如何选择这两种结构。</think>### SynchronizedMap ConcurrentHashMap 的设计目的及使用场景差异 --- #### 一、核心设计目的对比 1. **SynchronizedMap** - **目标**:通过**简单粗暴的全局锁**实现线程安全,为旧版非线程安全集合(如 `HashMap`)提供快速改造方案[^2]。 - **设计哲学**:牺牲并发性能,**优先保证强一致性**(所有操作原子化)。适用于对数据一致性要求极高但并发压力低的场景[^1]。 2. **ConcurrentHashMap** - **目标**:通过**分段锁(Java 7)或桶级锁(Java 8+)**实现高并发读写,平衡性能线程安全[^3]。 - **设计哲学**:接受弱一致性(如迭代器不保证实时最新数据),**优先保障吞吐量**,适合高并发且允许短暂数据不一致的场景[^3]。 --- #### 二、使用场景分析 | **场景特性** | **SynchronizedMap** | **ConcurrentHashMap** | |---------------------------|-------------------------------|------------------------------| | **低并发读写** | 简单易用,兼容旧代码 | 性能略优(CAS 减少锁开销) | | **高并发读写(如缓存)** | 性能急剧下降(全局锁竞争)[^3] | 高吞吐(分段/桶锁减少阻塞) | | **强一致性需求(如金融交易)** | 适用(操作原子化) | 不适用(弱一致性迭代) | | **混合操作(读写+迭代)** | 需外部同步,易引发死锁 | 天然支持安全并发迭代 | --- #### 三、为什么仍然需要 SynchronizedMap? 尽管 `ConcurrentHashMap` 在多数场景更优,但 `SynchronizedMap` 仍有存在价值: 1. **兼容性简单性** - 旧系统维护时,若已有代码依赖 `Collections.synchronizedMap()`,直接替换为 `ConcurrentHashMap` 可能引入兼容性问题。 - 简单场景(如单线程为主、偶尔低并发)中,全局锁的开销可忽略,且代码更易理解。 2. **强一致性保障** - 当业务要求所有线程看到 Map 的**完全一致状态**(如原子性批量操作),全局锁可避免分段锁可能导致的中间状态暴露[^1]。 3. **其他同步机制配合** - 若需在多个同步块中统一使用同一锁对象(如 `synchronized(mutex)` 控制多个资源),`SynchronizedMap` 的全局锁可简化设计。 --- #### 四、典型应用场景示例 1. **SynchronizedMap 适用场景** - 配置文件加载:低频更新,多线程读取需强一致性。 - 单例模式中的共享缓存:并发访问少,但要求绝对数据安全。 ```java Map<String, Config> configMap = Collections.synchronizedMap(new HashMap<>()); // 所有线程通过全局锁访问配置,避免加载过程中的中间状态 ``` 2. **ConcurrentHashMap 适用场景** - 实时计数器:高并发更新(如网站点击量统计)。 - 缓存系统:读写混合,允许短暂数据不一致。 ```java ConcurrentHashMap<String, AtomicInteger> counter = new ConcurrentHashMap<>(); counter.compute(key, (k, v) -> (v == null) ? new AtomicInteger(1) : v.incrementAndGet()); ``` --- #### 五、Java 版本演进的影响 - **Java 8+ 的优化**: `ConcurrentHashMap` 改用 `Node` + `CAS`/`synchronized`,锁粒度细化到单个桶,并引入红黑树优化哈希冲突,性能进一步提升[^3]。 - **趋势**: 随着高并发需求普及,`ConcurrentHashMap` 已成为主流选择,但 `SynchronizedMap` 仍在特定场景保留其“简单可靠”的优势。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值