synchronized 在高并发读场景下性能差,因其强制所有操作(含读)串行化,无读写区分,导致读线程排队阻塞;锁升级至重量级后引发频繁上下文切换;应改用 ReentrantReadWriteLock、volatile+不可变对象、StampedLock 或无锁结构。

synchronized 在高并发读取场景下性能差,核心问题不是“读本身慢”,而是它强制所有操作(包括读)串行化执行——哪怕多个线程只是安全地读取共享数据,也必须排队等待同一把锁。
读操作被无差别阻塞
Java 中 synchronized 方法或代码块没有读写区分。只要一个线程进入 synchronized 区域,无论它是读还是写,其他线程一律被挡在外面。比如:
反例:
public synchronized int getValue() { return count; }
100 个线程同时调用 getValue(),实际是 1 个 1 个执行,吞吐量暴跌,CPU 利用率却不高——大量线程在 monitor 等待队列中空转。
立即学习“Java免费学习笔记(深入)”;
锁升级加剧开销
当读请求密集、竞争持续存在时,JVM 会逐步将锁从偏向锁 → 轻量级锁 → 重量级锁升级。一旦升到重量级锁,就依赖操作系统 mutex,每次获取/释放都触发用户态与内核态切换,上下文切换成本陡增。
- 轻量级锁靠 CAS 和栈帧记录,适合低竞争
- 重量级锁需挂起线程、调度唤醒,单次操作耗时可达微秒级甚至更高
- 读多写少场景下,本可并发的读操作被迫承担写锁的全部代价
替代方案:读写分离更合理
对“读多写少”的典型场景(如配置缓存、统计计数器、状态快照),应避免用 synchronized,改用真正支持并发读的机制:
- ReentrantReadWriteLock:读锁可重入且共享,允许多线程同时读;写锁独占,保证修改安全
- volatile + 不可变对象:若读取的是简单值或不可变引用(如 final 字段、ImmutableList),volatile 就能保证可见性,零锁开销
- StampedLock:比 ReadWriteLock 更轻量,支持乐观读(validate),适合读远多于写的高频场景
- 无锁结构:如 AtomicInteger.get()、ConcurrentHashMap.get(),底层基于 CAS 或分段,读操作完全无锁
不改逻辑也能缓解:缩小同步范围
如果暂时无法替换锁机制,至少避免让读操作“裹挟”进大同步块:
- 不要对整个 getter 方法加 synchronized
- 把读操作移出 synchronized 块,只在真正需要写或复合操作时加锁
- 确认变量是否真的需要同步读——很多字段只需 volatile 保证可见性即可



















