ConcurrentHashMap树化过程不单独加锁,而是嵌套在桶级synchronized锁保护的putVal流程中;仅当持有桶头节点锁、链表长度≥8且数组容量≥64时,才调用treeifyBin完成链表转红黑树。

ConcurrentHashMap 的树化(即链表转红黑树)过程本身不直接加锁,但整个流程嵌套在写操作的同步保护中,通过“桶级 synchronized 锁 + volatile 可见性 + 状态检查”三重机制保障并发安全。
树化触发前已处于同步临界区
树化不是独立动作,而是 putVal 流程中链表遍历后的自然分支。当线程发现某桶链表长度 ≥8 时,会调用 treeifyBin 方法——但这个方法**只会在持有该桶头节点锁的前提下执行**:
- 若桶为空:先用 CAS 插入,无需锁,也无树化可能;
- 若桶非空:必须
synchronized(f)(f是桶头 Node 或 TreeBin),之后才进入插入/遍历逻辑; - 只有在锁内完成链表计数、确认长度达标、且数组容量 ≥64 后,才会真正调用
treeifyBin。
treeifyBin 内部不抢锁,但依赖已有锁和状态校验
treeifyBin 本身不重新加锁,而是复用外层已持有的桶锁。它会做两件事:
- 再次检查数组长度:若
tab.length ,则放弃树化,改为触发扩容(扩容由当前线程发起,其他线程可能协助); - 若满足树化条件,则新建
TreeBin对象,将原链表节点逐个封装为TreeNode,构建红黑树,并把TreeBin作为新桶头 —— 这一过程全程在synchronized(f)内完成,无其他线程可同时修改该桶。
TreeBin 作为树容器,统一管理读写同步
树化完成后,桶中存放的是 TreeBin 而非普通 TreeNode。这个设计很关键:
-
TreeBin是一个轻量级容器,持有一个root引用和读写状态标记(lockState); - 后续对该桶的所有写操作(如插入、删除)都会
synchronized(TreeBin),而不是锁某个 TreeNode; - 读操作仍无锁,靠
TreeBin.root和TreeNode字段的volatile语义保证可见性; - 这样既避免了红黑树旋转时频繁锁节点,又防止多线程并发构造或修改同一棵树导致结构错乱。
反树化(untreeify)同样受桶锁保护
当树中节点数 ≤6 时,会触发反树化(退化回链表)。这个过程也发生在 synchronized 桶锁内:
- 遍历
TreeBin中的TreeNode,重建普通Node链表; - 用 CAS 将新链表头原子替换掉原
TreeBin; - 整个替换是原子的,且发生在锁内,不会出现“一半树一半链表”的中间态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











