synchronized锁竞争导致线程阻塞的本质是“抢不到锁就排队等”,需同时满足同一把锁、并发到达、无锁可得三个条件;阻塞涉及jvm与os协同的状态切换,带来上下文开销、cpu虚高、延迟放大等问题;优化应聚焦缩小同步范围、避免全局锁、拆分锁对象及合理选用替代方案。

synchronized 的锁竞争直接引发线程阻塞,本质是“抢不到锁就排队等”。当多个线程同时尝试获取同一把锁(即锁对象相同),而该锁已被占用时,后续线程无法进入同步区域,会被挂起并加入该锁对应的等待队列,直到持有锁的线程释放锁、操作系统将其唤醒——这个过程就是线程阻塞。
锁竞争触发阻塞的条件
不是所有 synchronized 都会阻塞,只有满足以下三点才会发生真实竞争和阻塞:
- 同一把锁:多个线程调用的是同一个对象的 synchronized 实例方法,或显式使用同一个对象作为 synchronized 代码块的锁;静态方法则竞争 Class 对象锁。
- 并发到达:多个线程几乎同时执行到加锁点(如 monitorenter 指令),且锁未被释放。
- 无锁可得:锁处于被占用状态(对象头 Mark Word 标记为锁定),且当前线程不是锁的持有者(不可重入场景除外)。
阻塞不是立刻“睡死”,而是有状态切换
线程阻塞不是简单暂停,它涉及 JVM 和操作系统的协同:
- 线程从运行态转入阻塞态,放弃 CPU 时间片;
- JVM 将其放入该锁关联的等待队列(由操作系统维护);
- 释放锁时,JVM 通知操作系统,后者从中随机唤醒一个线程(不保证 FIFO);
- 被唤醒的线程需重新参与锁竞争,不一定立即获得锁。
阻塞带来的典型开销
频繁阻塞会显著拖慢系统,主要体现在:
- 上下文切换成本高:每次阻塞/唤醒都需保存和恢复寄存器、栈等,消耗 CPU 周期;
- CPU 利用率虚高:大量线程在等待队列中轮询调度,却不做有效工作;
- 响应延迟放大:请求需排队等待锁释放,P99 延迟可能陡增数倍;
- 容易掩盖热点问题:监控看到的是“线程卡住”,但根因是锁粒度太粗或对象被过度共享。
避免无效阻塞的关键思路
减少阻塞不等于去掉 synchronized,而是让锁更轻、更准、更短:
- 缩小同步范围:优先用 synchronized 代码块代替同步方法,只锁必要语句;
- 避免锁全局对象:如不要用 new Object() 作公共锁,更别用 String 或 Integer 等可能被复用的对象;
- 拆分锁对象:对集合类或计数器,可用分段锁(如 ConcurrentHashMap)或对象哈希分桶;
- 考虑替代方案:读多写少用 volatile,简单原子操作用 AtomicInteger,复杂逻辑再评估 ReentrantLock 或无锁结构。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











