
本文介绍如何为具有相同分区键(如 partition 字段)的对象实现线程安全的细粒度同步,避免全局锁竞争;核心是通过哈希映射动态管理分区级锁对象,并确保锁创建的线程安全性。
本文介绍如何为具有相同分区键(如 `partition` 字段)的对象实现线程安全的细粒度同步,避免全局锁竞争;核心是通过哈希映射动态管理分区级锁对象,并确保锁创建的线程安全性。
在高并发场景中,若对所有 Car 实例统一使用 synchronized(this) 或类级别锁,会导致无关分区间的线程无谓阻塞,严重降低吞吐量。理想的方案是:仅当两个 Car 对象的 partition 值相同时才互斥,否则并行执行。这要求为每个唯一 partition 值分配独立的锁对象,并保证锁对象的创建与获取过程本身线程安全。
以下是推荐的实现方式——使用 ConcurrentHashMap 替代普通 HashMap + 外层同步,既提升性能又简化逻辑:
import java.util.concurrent.ConcurrentHashMap;
public class CarManager {
// 使用 ConcurrentHashMap,天然支持线程安全的 computeIfAbsent
private final ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>();
private Object getLock(Car car) {
return lockMap.computeIfAbsent(car.partition, k -> new Object());
}
private void doSomething(Car car) {
Object lock = getLock(car);
synchronized (lock) {
// ✅ 此处仅对相同 partition 的调用串行化
// 例如:partition="A" 的多个 Car 可互斥执行;
// partition="B" 的 Car 可与 "A" 并发执行
}
}
}⚠️ 关键注意事项:
- 锁对象不可复用或销毁:一旦 partition 键被注册,其对应锁对象应长期存在(除非明确需清理)。若频繁增删 partition,可考虑定期清理空闲锁(如结合 WeakReference 或定时任务),但多数场景下无需主动回收。
- 避免锁泄漏风险:不要将 car.partition 直接作为锁(如 synchronized(car.partition)),因字符串常量池或 intern 行为可能导致意外共享锁,引发跨业务逻辑的不必要阻塞。
- 锁粒度权衡:ConcurrentHashMap 的分段锁机制已足够高效,但若 partition 值极多(如百万级),可考虑哈希分桶(如 lockMap.get(partition.hashCode() % N))以限制锁对象总数,防止内存膨胀。
✅ 总结:ConcurrentHashMap.computeIfAbsent() 是实现分区锁最简洁、高效且线程安全的方式。它消除了显式同步块,规避了竞态条件(如双重检查失败),同时保持了极佳的并发性能。该模式广泛适用于缓存更新、会话管理、订单处理等需按业务维度隔离并发的场景。
立即学习“Java免费学习笔记(深入)”;


















