java中synchronized通过偏向锁、轻量级锁与自适应自旋、锁消除与锁粗化四层优化减少高并发开销:偏向锁实现单线程零成本加锁;轻量级锁配合自适应自旋避免线程切换;锁消除移除无逃逸对象的同步;锁粗化合并连续锁操作。

Java 中 synchronized 在高并发下不是“一锁到底”,而是 JVM 通过多层动态优化来减少开销。核心思路是:能不加锁就不加,能轻量就不重量,能分治就不独占。
偏向锁:单线程场景的零成本入场
当一个对象首次被某个线程加锁时,JVM 会在对象头记录该线程 ID,后续该线程再次进入同步块无需任何 CAS 或互斥操作,直接通过标记验证即可执行。这使无竞争同步接近无锁性能。
适用典型场景:
- 单线程反复调用的初始化逻辑(如 Spring Bean 初始化)
- Web 请求中每个线程处理独立用户会话数据
- 线程局部缓存(ThreadLocal + synchronized 初始化)
注意:一旦其他线程尝试竞争,偏向锁会撤销并升级为轻量级锁,频繁撤销反而带来额外开销。可通过 -XX:-UseBiasedLocking 关闭。
轻量级锁与自适应自旋:短时竞争的“等待不切换”
当偏向锁失效后,JVM 尝试用 CAS 将对象头替换为指向当前线程栈帧中 Lock Record 的指针。若成功,即获得轻量级锁;失败则说明存在竞争,触发自旋等待。
自适应自旋不是固定次数,而是根据前一次在该锁上的自旋结果和持有者状态动态调整——若上次自旋成功,本次更可能延长;若上次已失败,则快速退回到重量级锁。
优势在于避免线程挂起/唤醒的内核态切换,适合临界区执行时间极短(通常微秒级)的场景。
锁消除与锁粗化:编译器级别的智能裁剪
这两项优化由 JIT 编译器基于逃逸分析自动完成,开发者无需编码干预,但需理解其前提:
-
锁消除:当同步对象的作用域仅限于当前方法、未逃逸出线程(如局部 StringBuilder、临时 Map),JIT 会直接移除
synchronized块 - 锁粗化:连续多次对同一对象加锁/解锁(如 for 循环内反复同步),JIT 会将它们合并为一次更长的锁持有,减少锁操作本身开销
例如:for (int i = 0; i 可能被粗化为 <code>synchronized(obj) { for (...) obj.add(i); }。
应用层主动优化:代码结构决定锁效率
JVM 的底层优化再强,也绕不开代码设计。真正影响高并发吞吐的,往往是开发者的三类实践:
- 缩短锁持有时间:把耗时操作(IO、计算、远程调用)移出同步块,只锁真正需要保护的共享状态变更
-
缩小锁粒度:用分段锁(如 ConcurrentHashMap 的 Segment 或 JDK8+ 的 Node 数组分桶)、读写分离(
ReentrantReadWriteLock)替代全局锁 -
避免热点对象:像计数器、全局配置、单例状态等高频争用对象,改用
LongAdder、StampedLock或无锁原子类,而非synchronized(this)
一个简单反例:public synchronized void doAll(String s) { parse(s); saveToDB(s); notifyAll(); } —— 三个动作中只有 saveToDB 涉及共享资源,其余都应放开。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











