hashmap多线程put非原子导致桶覆盖:判空与写入分离,多线程同时判空为null后写入,后写覆盖先写;哈希冲突加剧覆盖;size非volatile且++非原子,引发扩容延迟与更多覆盖。

因为 put 操作不是原子的,多个线程可能同时判断同一个桶(bucket)为空,并各自写入新节点,后写的直接覆盖先写的。
关键点:桶位判空与写入分离
HashMap 在插入前会先检查目标桶是否为空:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- if ((p = tab[i]) == null) —— 这行代码只是读操作,不加锁
- 紧接着 tab[i] = newNode(...) 是独立的写操作
- 两个线程几乎同时执行完第一行,都看到
tab[i]为null,于是都执行第二行 - 结果:第二个线程的 newNode 覆盖了第一个线程刚写入的节点
哈希冲突加剧覆盖风险
即使 key 不同,只要 hash 值落在同一个桶,就可能触发覆盖:
- 例如 key1 和 key2 经过
(n-1) & hash计算后得到相同下标 i - 线程 A 插入 key1 → 写入
tab[i] - 线程 B 同时插入 key2 → 也写入
tab[i],覆盖 key1 的节点 - 最终只有 key2 可被 get 到,key1 “丢失”
size 更新不同步导致连锁问题
size 字段未用 volatile 修饰,且自增非原子:
- 线程 A 读取 size=15,准备插入后执行
++size - 线程 B 同样读取 size=15,也执行
++size - 两次插入后 size 只变成 16(而非 17),影响扩容判断
- 扩容延迟可能让后续更多线程挤进同一桶,进一步放大覆盖概率
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










