核心思路是让锁管得更准、更小、更短:通过按数据结构分区(如桶级锁)、业务维度隔离(如用户id取模)、慎用复合操作、优化hashcode及合理设置初始化参数,实现高并发下的低竞争与高效执行。

核心思路不是“少用锁”,而是“让锁管得更准、更小、更短”。锁粒度拆分的本质,是把原本一把大锁保护的整块数据,按访问模式切分成多个互不干扰的小单元,每个单元配一把独立锁——这样多个线程操作不同单元时,完全并行,不争抢。
按数据结构天然分区:桶级锁与分区队列
ConcurrentHashMap 是最典型的范例。JDK 8+ 不再锁整个表,而是只对哈希数组中某个 bucket 的头节点加 synchronized 锁。这意味着:
- 只要 key 哈希分布均匀,16 个线程写 16 个不同 bucket,就是纯并行,零阻塞
- 空桶插入直接 CAS,全程无锁;仅冲突时才进同步块
- 同理,自研任务队列可将单队列拆成 N 个独立子队列(如 PartiFlow),入队/出队按 key 或轮询路由到指定分区,锁竞争直接降为原来的 1/N
按业务维度逻辑隔离:避免跨域争抢
技术层的锁拆分必须配合业务建模。例如用户积分系统:
- 不要用一把全局锁更新所有用户余额,而应按 userId 取模(如 % 64)映射到 64 个独立计数器或分片 Map
- 订单状态变更可按订单号哈希分组,同一订单的多次更新串行,不同订单完全并发
- 缓存预热任务按业务域(商品、营销、用户)拆成独立调度线程池,避免互相卡住
慎用复合操作,控制锁内耗时
细粒度锁效果会被长临界区抵消。像 computeIfAbsent、merge 这类方法,虽原子性强,但函数体执行期间一直持有桶锁:
- 若函数内含远程调用、复杂计算或数据库查询,锁会持数毫秒,严重拖慢其他线程
- 推荐策略:先 get 判断是否存在,不存在再显式 put;或把重逻辑移出同步块,只在锁内做轻量状态变更
- 自定义 key 类务必重写高效、高离散度的 hashCode(),否则大量 key 落同一桶,再细的锁也白搭
参数与初始化要匹配真实负载
锁粒度优势需要底层容量支撑:
- ConcurrentHashMap 的 initialCapacity 应按预估总 key 数 ÷ 0.75 向上取整(如 10 万条 → 设 131072),避免早期扩容引发全表重哈希和锁升级
- JDK 7 的 concurrencyLevel 仅作 Segment 数估算参考;JDK 8+ 已忽略,但旧代码传入过大值可能误导初始容量计算
- 分区队列的分区数不宜盲目求多——太少起不到分流效果,太多则调度开销上升、缓存局部性下降,通常 4~64 之间根据 CPU 核心数和热点分布测试确定
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











