大家好,我是程序员二叉。
简介
本篇详解ConcurrentHashMap JDK7/JDK8底层差异、放弃分段锁原因、扩容机制;对比ReentrantLock与synchronized区别,拆解ReentrantLock可重入/公平/中断/超时四大特性AQS实现原理,面试高频必背。欢迎点赞关注收藏。
一、ConcurrentHashMap JDK7 工作流程
- 采用Segment分段锁架构,整体由Segment数组组成,每个Segment是独立小型HashTable
- 存储结构:Segment[] → 内部HashEntry数组+单向链表
- 存取流程:
- key哈希取模定位对应Segment
- 获取Segment独占锁
- 二次哈希定位桶位,遍历链表增删改查
- 优势:相比Hashtable全局锁,多Segment可并发操作,并发能力提升
二、ConcurrentHashMap JDK8 工作流程
- 彻底移除Segment,顶层直接是Node数组;存储结构:数组+链表+红黑树
- 寻址:hash扰动计算哈希值定位桶下标
- 插入逻辑:
- 桶位为空:CAS无锁直接放入Node
- 桶位有数据:对桶头节点加
synchronized锁 - 链表长度≥8且数组容量≥64:链表转红黑树;元素减少退化为链表
- 查询全程无锁,依靠volatile保证可见性;size采用baseCount+CounterCells自旋计数
三、JDK7 与 JDK8 ConcurrentHashMap 核心区别
- 锁粒度
JDK7:Segment大分段锁;JDK8:桶级细粒度synchronized+CAS - 数据结构
JDK7:数组+链表;JDK8:数组+链表/红黑树,长链表查询速度大幅提升 - 计数方式
JDK7:每个Segment单独维护count;JDK8:baseCount + 分段CounterCells自旋统计 - 扩容模式
JDK7:单个Segment独立扩容,互不干涉;JDK8:全局统一扩容,多线程协助迁移数据
四、JDK8 为什么废弃分段锁,改用CAS + synchronized
- Segment内存开销大:16个分段自带锁、计数、对象头,占用大量堆内存;桶锁轻量化无额外开销
- 并发吞吐量更高:同Segment下多桶无法并行,桶级锁不同哈希桶完全并发
- synchronized锁升级优化成熟:偏向/轻量/重量级三层升级,低竞争场景性能不输AQS锁
- 扩容效率提升:支持多线程一起迁移数据,分段锁只能单Segment独自扩容
- 代码结构简化:去掉多层嵌套Segment,逻辑简洁易维护
五、ConcurrentHashMap JDK8 扩容机制
- 触发阈值:元素数量达到
容量 * 0.75负载因子 - 新table容量直接扩容为原来2倍
- 初始单线程创建新数组,之后其他读写线程可协助迁移旧桶数据
- 迁移中旧桶标记fwd占位节点,读写碰到fwd会参与协助扩容
- 数据迁移二分:key哈希结果只有原下标i 或 i+旧容量两个去向
- 全部桶迁移完毕,替换全局table引用,扩容结束
六、ReentrantLock 和 synchronized 核心区别
- 底层实现
synchronized:JVM监视器Monitor,代码块结束自动释放锁;
ReentrantLock:基于AQS的API层锁,必须手动lock()/unlock() - 中断等待
synchronized:阻塞时不可中断,只能死等;
ReentrantLock:lockInterruptibly()支持中断抛出异常退出等待 - 公平锁选择
synchronized:永久非公平锁;
ReentrantLock:构造传true开启公平锁,默认非公平 - 超时等待
synchronized:无超时机制;
ReentrantLock:tryLock(时间,单位)超时拿锁直接返回false - 多条件队列
synchronized:只有1套wait/notify队列;
ReentrantLock:可创建多个Condition,精准唤醒指定线程组 - 释放风险
ReentrantLock必须在finally执行unlock,否则极易永久死锁;synchronized自动释放
七、ReentrantLock四大特性AQS底层实现
1. 可重入实现
AQS中state数值记录重入次数;同一线程重复获取锁state+1;释放逐层state-1,state=0才算完全释放锁。
2. 可中断实现
lockInterruptibly()阻塞park前检测中断标识;收到中断直接抛异常,清理队列节点终止等待。
3. 超时等待实现
tryLock(long, TimeUnit)自旋时记录截止时间,时间耗尽直接放弃竞争返回false。
4. 公平锁实现
公平模式acquire前先判断阻塞队列有无前驱等待节点,有则直接入队不抢先CAS;非公平模式上来直接抢锁。
面试速记总结
- JDK7分段锁、JDK8桶级synchronized+红黑树,细粒度锁吞吐更高
- JDK8多线程协作扩容,负载因子固定0.75
- ReentrantLock手动AQS锁,支持公平/中断/超时/Condition;synchronized是JVM自动监视器锁