carrier thread是jvm调度虚拟线程所依赖的底层os线程,本质为forkjoinpool中的workerthread,支持任务窃取与高效挂起/恢复,不固定绑定虚拟线程,由jvm动态映射与切换。

Java 虚拟线程(Virtual Thread)本身不直接绑定操作系统线程,而是由 JVM 在用户态调度,其执行最终需要映射到实际的 OS 线程上——这个承载虚拟线程运行的 OS 线程,就叫 Carrier Thread(载体线程)。JVM 默认使用一个内部的 ForkJoinPool(非 public 的 ForkJoinPool.commonPool() 的变体)来管理这些载体线程,并负责调度虚拟线程的执行。
Carrier Thread 本质是 ForkJoinWorkerThread
每个 Carrier Thread 实际上是一个 ForkJoinWorkerThread 的子类实例(JVM 内部实现,如 java.lang.VirtualThread$VThreadRunner),它继承了 ForkJoinPool 的工作线程能力:支持 work-stealing(任务窃取)、轻量级挂起/恢复、以及高效的队列操作。这使得 Carrier Thread 能高效复用,避免频繁创建销毁 OS 线程。
- JVM 启动时会初始化一个专用的 ForkJoinPool(通常称 “virtual thread scheduler pool”),默认并行度为
Runtime.getRuntime().availableProcessors() - 该池不对外暴露,也不参与应用层的
ForkJoinPool.commonPool()任务调度 - 所有虚拟线程的首次执行、阻塞后恢复、或被唤醒,都通过向该池提交一个“运行任务”(
Runnable或内部VThreadTask)来触发
虚拟线程如何绑定与切换 Carrier Thread
虚拟线程在生命周期中可能多次切换 Carrier Thread,但对开发者完全透明:
-
启动时:调用
vthread.start()或Thread.ofVirtual().start(runnable),JVM 将其封装为一个可提交任务,放入 ForkJoinPool 队列;池中空闲的 Carrier Thread 取出并执行它 -
遇到阻塞调用时(如
Object.wait()、Thread.sleep()、文件 I/O、网络 read/write 等):JVM 拦截该调用,主动将当前虚拟线程置为WAITING或BLOCKED状态,并让 Carrier Thread 退出当前虚拟线程的执行上下文,去执行其他任务(或窃取任务) - 阻塞结束时(如 I/O 完成、sleep 超时、notify 唤醒):JVM 将虚拟线程重新包装为任务,再次提交进同一 ForkJoinPool;某个 Carrier Thread(不一定是原来的)取出并继续执行
ForkJoinPool 提供的关键调度能力
为什么选 ForkJoinPool 而不是普通线程池?核心在于它天然适配虚拟线程的高并发、短任务、频繁挂起/恢复的模式:
- Work-stealing 队列:每个 Carrier Thread 拥有双端队列(Deque),新任务插入队尾,窃取时从队首取任务,降低竞争;适合大量轻量虚拟线程快速分发
- 无锁入队/出队:FJP 内部大量使用 CAS 和线程局部状态,减少同步开销,匹配虚拟线程“快进快出”的特征
-
自动伸缩(有限):当任务积压且线程都在忙时,FJP 可临时创建新 Carrier Thread(受
jdk.virtualThreadScheduler.maxPoolSize限制);空闲一段时间后也会回收 - 嵌套调度友好:虚拟线程内若再启动新虚拟线程,新任务仍提交到同一 ForkJoinPool,保持调度一致性
可观察与调优的相关机制
虽然细节封装较深,但可通过以下方式了解或影响 Carrier Thread 行为:
- 通过 JVM 参数调整:
-Djdk.virtualThreadScheduler.parallelism=N(初始并行度)、-Djdk.virtualThreadScheduler.maxPoolSize=M(最大 Carrier 数) - 监控指标:使用 JFR(Java Flight Recorder)开启
jdk.VirtualThreadSubmitFailed、jdk.VirtualThreadParked等事件,观察调度压力 - 注意:不要手动向
ForkJoinPool.commonPool()提交虚拟线程任务——它不感知虚拟线程语义,也无法正确处理挂起/恢复逻辑
Carrier Thread 的调度本质是 JVM 利用 ForkJoinPool 成熟的 work-stealing 框架,在用户态构建了一层高效的、可扩展的线程执行基础设施。它不改变 OS 线程模型,却大幅提升了高并发场景下线程资源的利用率和响应性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











