synchronized初始采用轻量级锁+cas自旋抢锁,全程用户态执行,不触发内核态切换与线程挂起;cas成功即获取锁,失败则自旋等待,仅当自旋超限或持有线程阻塞时才升级为重量级锁并挂起。

synchronized 在 Java 中并不是一开始就要挂起线程,它会先尝试用轻量级锁 + CAS 自旋的方式抢锁——这个过程完全在用户态完成,不进内核、不切状态、不调度,自然就避开了线程挂起。
轻量级锁怎么靠 CAS 抢到锁而不挂起
当一个线程进入 synchronized 同步块时,JVM 会检查对象头是否为“无锁”状态(标志位是 01):
- 如果是,就在当前线程的栈帧里新建一个「锁记录」,然后用 CAS 尝试把对象头的 Mark Word 替换为指向该锁记录的指针;
- CAS 成功 → 锁获取完成,线程继续执行,全程没挂起、没排队、也没进等待队列;
- CAS 失败 → 说明锁正被别的线程持有,此时不立刻阻塞,而是进入自旋等待阶段。
自旋等待为什么不算挂起
自旋不是“干等”,而是反复执行 CAS 检查对象头是否已恢复无锁状态或被释放:
- 每次检查都是纯用户态指令,不触发系统调用,不切换线程状态;
- 只要持有锁的线程很快释放(比如临界区只有几条赋值语句),自旋线程大概率在下一次 CAS 就能抢到,根本不需要挂起;
- 这种机制把“挂起”推迟到真正等不及的时候——比如自旋次数超阈值、或检测到持有者已阻塞,才升级为重量级锁并挂起线程。
JVM 怎么让自旋更聪明
从 JDK 1.6 起,默认启用自适应自旋,不是死循环硬等:
- 如果上次在同一把锁上自旋成功,且持有锁的线程还在运行中,这次就多旋几次;
- 如果上次自旋失败,或持有者已进入阻塞态,就快速退避,避免空耗 CPU;
- 单次自旋建议控制在 10–100 次之间,具体取决于临界区平均耗时:越短越值得多试几次,超过 1 微秒就该考虑早退。
关键点:挂起是最后手段,不是默认动作
轻量级锁 + CAS 自旋的本质,是把悲观等待变成乐观试探。它不承诺“永不挂起”,但把挂起延迟到竞争真实持续、且短时无法解决时才发生。这对锁持有时间短、竞争不激烈但频次高的场景(如高频计数、对象池分配)特别有效——线程状态切换开销动辄数百纳秒,而一次 CAS 只需几个 CPU 周期。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











