重量级锁触发上下文切换的真实起点是自旋失败后调用futex_wait进入内核态,而非进入synchronized块即切换;此时发生寄存器保存、栈切换、运行队列移出及tlb/cache同步,伴随缓存失效与跨核同步等连锁开销。

Java 中 synchronized 在重量级锁模式下,本质是把线程控制权交给了操作系统,用的是真实的互斥锁(Mutex Lock),这个过程必然引发用户态到内核态的切换——这才是上下文切换开销的真正来源,而不是“锁本身慢”。
重量级锁触发上下文切换的真实时机
不是一进 synchronized 块就切内核态。JVM 先让线程在用户态自旋(默认约 10 次),只有自旋失败后,才调用 futex_wait 系统调用,这时才真正进入内核态挂起线程。也就是说:
- 自旋阶段不涉及上下文切换,开销小、可控;
-
futex_wait调用那一刻,才是上下文切换的物理起点; - 一次
futex_wait意味着保存寄存器、切换栈、移出 CPU 运行队列、同步 TLB 和 cache line(跨核时更贵)。
为什么开销远不止“几百纳秒”那么简单
单次 pthread_mutex_lock/unlock 系统调用实测耗时在几百纳秒到微秒级,但真正伤性能的是连锁副作用:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 前一个线程执行完后,其热点数据大概率被踢出 L1/L2 缓存,新线程唤醒后 cache miss 暴增;
- 若等待线程和唤醒线程落在不同物理核,TLB 和缓存行同步耗时比同核高 2~3 倍;
- 即使
synchronized块里只写return;,只要升级为重量级锁,这些开销照常发生。
怎么判断是不是重量级锁在拖慢系统
不能只看 jstack 里一堆 BLOCKED 线程——它们未必全已切换。关键看操作系统级指标:
-
involuntary_ctxt_switches:用perf stat -e context-switches,involuntary_ctxt_switches,task-clock -p <pid></pid>抓取,这个值反映“被迫让出 CPU”的次数,才是锁争用的直接证据; -
cs列持续高于 10k/s(vmstat 1):空闲系统通常低于 1k,高 cs + 低 CPU 利用率 + 吞吐骤降,是典型信号; - Safepoint 停顿激增:加参数
-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1,Monitor 争用活跃时 safepoint 频次明显上升。
监控和排查要避开的常见误区
生产环境别用 strace -e trace=futex——futex 调用频率极高,开启后自身就会拖慢服务。它只适合复现环境定位锁热点。真正该抓的是 Java 进程粒度的上下文切换数据,避免被其他进程噪声干扰。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










