Java 8中ConcurrentHashMap的TreeBin写入加锁时为何可执行find读取操作?
关于ConcurrentHashMap TreeBin锁与find()方法无阻塞的解答
这问题问到点子上了,其实核心在于ConcurrentHashMap针对红黑树节点(TreeBin)的读写分离设计,以及Java中synchronized锁的作用范围限制,下面拆解来说:
1. synchronized锁只限制同步代码块的执行,不阻止无锁读操作
你看到的写操作里synchronized (f)(这里f是TreeBin实例),它的作用是保证同一时刻只有一个线程能进入这个同步代码块执行写逻辑(比如putTreeVal)。但synchronized是互斥锁,它只针对“想要获取同一对象锁的同步代码”生效——换句话说,没有加锁的方法(比如find())完全可以直接访问TreeBin对象,不需要等待锁释放,因为它根本没尝试去拿锁。
2. TreeBin的find()是无锁乐观读设计,自带降级逻辑
看你贴的find()方法注释:“尝试从根节点开始使用树结构比较进行搜索,但在无法获取锁时转为线性搜索”。这里的“无法获取锁”不是指synchronized锁,而是TreeBin内部维护的volatile类型的锁状态标记(lockState):
- 当写操作持有
synchronized锁修改红黑树时,会同时更新lockState标记树处于修改状态; find()执行时会先尝试乐观读取红黑树节点(利用volatile的可见性保证读到最新状态),如果检测到树正在被修改,它会自动降级为遍历TreeBin内部的链表结构(TreeBin本身是链表+红黑树的混合结构),这个遍历过程完全无锁,自然不需要等待写锁释放。
3. 结合代码再理一遍流程
- 写操作:通过
synchronized锁住TreeBin,执行putTreeVal修改红黑树,此时其他线程的写操作会被阻塞,但读操作不受影响; - 读操作:
find()先尝试红黑树搜索,若发现树在修改则切换到链表遍历,全程不申请锁,所以能正常执行,不用等写锁释放。
这种设计就是ConcurrentHashMap能兼顾线程安全和读写性能的关键——读操作尽量无锁,写操作仅锁定必要的最小范围,避免不必要的阻塞。
内容的提问来源于stack exchange,提问作者LuoNet
相关产品推荐
相关产品推荐

