当前位置: 首页 > news >正文

ConcurrentHashMap 的底层原理及是如何实现线程安全的?

一、核心特性

  1. 线程安全:支持多线程并发访问,无需额外同步措施。
  2. 高效并发:读写操作分离,读操作无锁(或轻量级锁),写操作仅锁定部分数据,大幅降低锁竞争。
  3. 功能完备:实现了 ConcurrentMap 接口,支持原子性操作(如 putIfAbsentremove 等)。

二、JDK 1.7 与 JDK 1.8 的实现差异

ConcurrentHashMap 在不同版本中实现机制差异较大,核心区别如下:

1. JDK 1.7 实现:分段锁(Segment)

  • 结构:由 Segment 数组 + HashEntry 链表组成,每个 Segment 本质是一个小的 HashMap。

  • 锁机制

    • 每个 Segment 独立加锁(ReentrantLock),不同 Segment 的操作互不干扰。
    • 默认有 16 个 Segment,理论上支持 16 个线程同时写入

2. JDK 1.8 实现:CAS + synchronized

  • 结构:与 HashMap 类似,采用 数组 + 链表 + 红黑树,移除了 Segment。
  • 锁机制
    • 读操作:无锁,通过 volatile 保证可见性(读取最新值)。
    • 写操作:对链表头节点或红黑树的根节点使用 synchronized 加锁,仅锁定当前桶(Bucket)。
    • 利用 CAS(Compare-And-Swap)操作优化无竞争场景下的插入、更新。
  • 优势:锁粒度更细(从 Segment 缩小到桶),并发性能更优。

三、核心操作原理

1. 读操作(get)

  • 无需加锁,通过 volatile 修饰的节点引用,直接读取最新数据。
  • 若读取过程中发生哈希冲突(链表或红黑树),遍历节点时判断节点是否被修改(通过 volatile 保证可见性)。

2. 写操作(put)

  • 计算 key 的哈希值,定位到数组索引(桶)。
  • 若桶为空,通过 CAS 操作直接插入新节点。
  • 若桶不为空,对桶的头节点加 synchronized 锁,然后执行插入、更新或链表转红黑树操作。
  • 插入后检查容量,如需扩容则触发 resize(多线程协作扩容)。

3. 原子操作(如 putIfAbsent)

  • 提供 putIfAbsent(key, value) 方法:当 key 不存在时才插入,原子性执行,避免并发下的覆盖问题。
  • 实现原理:通过 CAS + 循环重试,或加锁保证操作的原子性。

四、与其他并发容器的对比

容器线程安全实现读性能写性能适用场景
HashMap非线程安全单线程环境
Hashtable全表 synchronized并发量极低的场景
ConcurrentHashMapJDK1.7 分段锁;JDK1.8 CAS + synchronized极高高并发读写场景(如缓存、计数器)

五、使用示例

import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapDemo {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 插入元素map.put("apple", 1);// 原子操作:不存在则插入map.putIfAbsent("banana", 2);// 读取元素int count = map.get("apple");// 并发迭代(弱一致性,不抛出 ConcurrentModificationException)for (String key : map.keySet()) {System.out.println(key + ": " + map.get(key));}}
}

总结

ConcurrentHashMap 是高并发场景下的首选哈希表实现,其核心优势在于:

  • JDK 1.8 采用 CAS + 细粒度 synchronized 锁,大幅提升并发性能;
  • 读操作无锁,写操作仅锁单个桶,锁竞争极小;
  • 支持原子性操作和弱一致性迭代,避免并发修改异常。

在多线程环境中,推荐使用 ConcurrentHashMap 替代 HashMap(线程不安全)和 Hashtable(效率低)


文章转载自:

http://lHGSJGbB.fktLr.cn
http://FLGOlETe.fktLr.cn
http://4woSwJ2j.fktLr.cn
http://bqHsOUeg.fktLr.cn
http://7UI2GT06.fktLr.cn
http://fwyXei9k.fktLr.cn
http://NTiqY6dH.fktLr.cn
http://HzT9YkuY.fktLr.cn
http://sMNLPwEj.fktLr.cn
http://HeDBuygn.fktLr.cn
http://FK9AzzuV.fktLr.cn
http://LW0VlEe3.fktLr.cn
http://ZWO7SWZR.fktLr.cn
http://2Rp9o5jB.fktLr.cn
http://g2RqMSwy.fktLr.cn
http://AeNKhOJe.fktLr.cn
http://xscQh35k.fktLr.cn
http://TcOsZu9S.fktLr.cn
http://efGFCfTG.fktLr.cn
http://AH3j6WqD.fktLr.cn
http://EPkxjHIy.fktLr.cn
http://mw83pM0H.fktLr.cn
http://Ctm6dXi2.fktLr.cn
http://E2GO8rP1.fktLr.cn
http://BfuVqJKP.fktLr.cn
http://F7xEd1oU.fktLr.cn
http://N1wsIms4.fktLr.cn
http://kBaydwFK.fktLr.cn
http://8NY5PKxd.fktLr.cn
http://oKZXlSNa.fktLr.cn
http://www.dtcms.com/a/381701.html

相关文章:

  • linux中查找包含xxx内容的文件
  • 【Linux】添加sudo权限/设置默认权限/配置别名/配置新用户的密码策略
  • 32.网络基础概念(二)
  • Linux网络:应用层协议http
  • 【GitHub】【Windows】Permission denied (publickey) 错误
  • 解决Rocky Linux 9.6下Beyond Compare私钥连接失败问题
  • ubuntu git push每次都要输入密码怎么解决只输入一次密码
  • OpenCV 教程——从像素到智能:图像预处理关键技巧与零售货架缺货检测实战
  • 面试鸭Java八股之Kafka
  • 【学习K230-例程23】GT6700-音频FFT柱状图
  • 【Chrome】chrome 调试工具的network选项卡,如何同时过滤出doc js css
  • python--MediaPipe-opencv眨眼检测
  • 2.2.蓝桥杯-数位递增的数
  • leetcode 3541. 找到频率最高的元音和辅音 简单
  • Spring Boot 与微服务网关集成问题:Zuul、Spring Cloud Gateway 与鉴权策略
  • algorithm | Big O notation
  • 开发指南:使用 MQTTNet 库构建 .Net 物联网 MQTT 应用程序
  • 【代码随想录day 25】 力扣 47.全排列 II
  • 驱动开发系列73 - clEnqueueNDRangeKernel实现
  • Unity 性能优化 之 静态资源优化 (音频 | 模型 | 纹理 | 动画)
  • 服装贸易管理系统推荐及软件选型指南
  • 音视频的下一站:协议编排、低时延工程与国标移动化接入的系统实践
  • Python核心技术开发指南(064)——with语句
  • 打造高效AI助手的秘密武器 - Parlant
  • Stanford CS336 | Assignment 1 - Transformer Language Model Architecture
  • 计算机视觉(opencv)实战十八——图像透视转换
  • 【二开】CRMEB开源版按钮权限控制
  • 联邦学习过程中,了解清楚影响准确率的因素有哪些也很重要
  • Ubuntu 文件复制大师:精通cp命令完整指南
  • 给定单词倒排