读写分离应优先选用ReentrantReadWriteLock,其次高并发只读场景用StampedLock,简单状态更新则用volatile或AtomicXxx;避免用synchronized和ReentrantLock,因其无法支持读并发。

读写分离场景下,核心矛盾是:读操作多、并发高,写操作少但需独占;选锁的关键是让读不阻塞读,读写/写写相互阻塞,同时避免过度竞争和内存开销。
优先考虑 ReentrantReadWriteLock
这是 JDK 提供的标准化读写锁实现,语义清晰、功能完整,适用于绝大多数读多写少的业务场景(如缓存加载、配置管理、报表元数据访问)。
- 支持公平与非公平模式:默认非公平,吞吐高;若写饥饿严重(大量读长期霸占,写一直等),可启用公平模式(构造时传
true) - 读锁可重入,写锁也可重入,且写锁能降级为读锁(但读锁不能升级为写锁,避免死锁)
- 注意:持有写锁时,其他线程无法获取读锁;持有读锁时,写锁会阻塞——这正是读写分离所需的语义
高并发只读 + 极低频写 → 考虑 StampedLock
当读操作达到数万 QPS、对延迟极度敏感,且写操作极少(如分钟级更新的统计指标),StampedLock 的乐观读机制可显著减少 CAS 和队列开销。
- 乐观读(
tryOptimisticRead())不加锁,仅校验版本戳;若期间无写,直接返回结果,零同步成本 - 若校验失败,再退化为悲观读锁(
readLock()),仍比传统读锁轻量 - ⚠️ 注意:它不支持重入,也不支持条件变量(
Condition),且使用稍复杂,需手动处理戳验证逻辑
简单共享状态 + 无复杂同步需求 → volatile + CAS 更轻量
如果“读写分离”实际只是读一个简单对象(如 int、long、不可变引用),且写操作是原子更新(如计数器自增、状态切换),无需锁的语义保障,直接用 volatile 或 AtomicXxx 类更高效。
立即学习“Java免费学习笔记(深入)”;
-
volatile保证可见性与有序性,适合只读+单次写(如初始化后只读的配置对象) -
AtomicInteger、AtomicReference等提供无锁原子更新,适合高频读 + 低频 CAS 写 - 优势:无锁开销、无线程挂起、GC 友好;劣势:无法用于复合操作(如“先读再判再写”需额外同步)
慎用 synchronized 和 ReentrantLock 做读写分离
它们是独占锁,读读之间也会串行化,彻底破坏读并发性,在真正读多写少的场景下会成为性能瓶颈。
- 除非读操作极少(比如读占比
- 若必须用,可通过分段(如
ConcurrentHashMap分段思想)或读写标志位 + CAS 手动实现,但复杂度高、易出错,不推荐


















