虚拟线程和锁


虚拟线程对 synchronized、ReentrantLock 这类 “锁”,并没有做特殊 “自动优化”,反而会有坑。

只有一类特殊情况:阻塞在 java.util.concurrent.locks park 等待时,虚拟线程才会像 IO 阻塞一样,让出平台线程。

下面用 Java 开发最容易遇到的场景,讲清楚虚拟线程 + 锁到底怎么回事。


1. 先分清:虚拟线程真正能优化的 “阻塞”

虚拟线程只有在遇到 JVM 能识别的 “挂载点” 时,才会释放底层平台线程:

  • Socket / 文件 IO 阻塞
  • sleep()
  • LockSupport.park()(AQS 底层)
  • Object.wait()

这些都会触发:虚拟线程挂起 → 平台线程释放


2. 重点:synchronized 关键字 → 虚拟线程不会优化!

这是最重要的坑。

synchronized (lock) {
    // 临界区
}

虚拟线程进入 synchronized 阻塞时:

  • 不会释放平台线程
  • 平台线程会被卡住、占着不放
  • 跟传统平台线程表现完全一样
  • 高并发下,大量虚拟线程阻塞在 synchronized 上 → 平台线程被耗尽,吞吐量上不去

原因:synchronized 是 JVM 内部的 monitor 锁,目前 OpenJDK 还没有把它改造为 “虚拟线程友好” 的挂起机制。

一句话:synchronized 在虚拟线程下,仍然是重量级阻塞,不释放平台线程。


3. ReentrantLock / ReentrantReadWriteLock → 虚拟线程可以优化!

java.util.concurrent.locks` 包下的锁,底层是:`LockSupport.park()` / `unpark()

这是 JVM 明确识别的挂载点

private final ReentrantLock lock = new ReentrantLock();

public void func() {
    lock.lock();   // 竞争不到锁时 → park()
    try {
        // ...
    } finally {
        lock.unlock();
    }
}

虚拟线程竞争不到锁时:

  • 调用 LockSupport.park()
  • 虚拟线程被挂起
  • 底层平台线程释放,去执行别的虚拟线程

这就是虚拟线程对锁的唯一真实优化


4. Object.wait () /notify () → 也能优化

synchronized (obj) {
    obj.wait(); // 会 park → 虚拟线程挂起,释放平台线程
}

wait() 会让虚拟线程挂起并让出平台线程,没问题。但再次强调:抢锁那一步的 synchronized 本身不优化。


5. 总结一张表

锁方式虚拟线程阻塞时是否释放平台线程虚拟线程友好度
synchronized不释放❌ 不友好
ReentrantLock释放✅ 友好
ReentrantReadWriteLock释放✅ 友好
Object.wait()释放✅ 友好
sleep()释放✅ 友好

6. 真实工程建议(非常关键)

在 Spring Boot 3.2+ 开启虚拟线程后:

  1. 高并发路径下,尽量把 synchronized 替换成 ReentrantLock否则虚拟线程优势会大打折扣。
  2. 不要以为开了虚拟线程,锁就自动变快synchronized 是虚拟线程最大性能陷阱之一
  3. 如果你用的是 Spring 事务、缓存、工具类里隐式带 synchronized高并发下依然会形成平台线程瓶颈。

7. 一句话终极总结

  • synchronized:虚拟线程不优化,阻塞时占着平台线程不放
  • ReentrantLock 等 JUC 锁:虚拟线程会优化,阻塞时自动让出平台线程

这就是虚拟线程对 “锁” 的全部真实行为。

JAVA-技能点
多线程