前言
虚拟线程在JDK19得时候已经推出,在预览模式中即可使用,在JDK21中虚拟线程正式成为标准特性,今天就主要介绍一下虚拟线程的使用方法。
一、虚拟线程介绍
虚拟线程是轻量级的线程,与平台线程不同的是虚拟线程由 JVM 管理而不是操作系统直接管理,这使得创建数百万个虚拟线程成为可能,而不会产生传统平台线程的开销,虚拟线程实际上运行在平台线程上,多个虚拟线程可以共享同一个平台线程,虚拟线程的概念和python的携程是同一类概念。虚拟线程利用了 Java 的 Fork-Join 框架来调度和管理,Fork-Join 框架提供了一个工作窃取(work-stealing)算法,可以高效地在不同的载体线程之间分配任务,JVM 内部使用 FIFO 队列来维护就绪状态的虚拟线程。
主要优势
| 优势 | 说明 |
|---|---|
| 高吞吐量 | 虚拟线程非常轻量,可以轻松创建大量线程来处理并发任务 |
| 降低资源消耗 | 相比于平台线程,虚拟线程占用的内存更少,上下文切换开销更小 |
| 简化并发编程 | 可以使用简单的阻塞式 I/O 操作,而不用担心阻塞宝贵的平台线程 |
| 更好的伸缩性 | 应用程序可以更好地扩展以处理大量并发连接 |
二、虚拟线程应用场景
虚拟线程适用于I/O密集型任和阻塞式任务。虚拟线程的主要设计目标是处理大量I/O密集型任务,如网络请求、数据库访问等;虚拟线程有透明的挂起和恢复机制,当虚拟线程执行阻塞操作时,JVM会自动将其从平台线程上解绑,让平台线程可以执行其他任务
虚拟线程适用于阻塞式任务
注意:
避免用于 CPU 密集型任务:对于计算密集型任务,传统的平台线程可能更合适
结合 CompletableFuture 使用:可以与 CompletableFuture 结合使用,构建复杂的异步处理流程
三、虚拟线程创建方式和用法
(一)使用Thread.ofVirtual()创建
java
Thread virtualThread = Thread.ofVirtual()
.name("虚拟线程")
.start(() -> {
System.out.println("下次呢行名: " + Thread.currentThread());
});
(二)使用Thread.startVirtualThread()创建
java
Thread.startVirtualThread(() -> {
System.out.println("执行");
});
(三)使用Executors创建(推荐使用此方式来构建)
java
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
Future<String> future = executor.submit(() -> {
Thread.sleep(1000); // 不会阻塞平台线程
return "结果";
});
try {
//获取结果
System.out.println(future.get());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
注意: 为什么推荐使用使用Executors创建虚拟线程?
ExecutorService 提供了统一的任务调度机制,能够自动管理虚拟线程的生命周期,避免手动创建和销毁线程带来的复杂性。
通过 Executors.newVirtualThreadPerTaskExecutor() 创建的虚拟线程池,会在任务完成后自动回收资源,减少内存泄漏风险。
四、案例Demo
java
public static void main(String[] args) {
//使用Executors构建百万虚拟线程
long startTime = System.currentTimeMillis();
//使用CountDownLatch统一返回结果
CountDownLatch latch = new CountDownLatch(1000000);
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1000000; i++){
executor.submit(() -> {
try {
Thread.sleep(1000); //模拟任务执行1s
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
latch.countDown(); // 每个任务完成后计数减一
});
}
try {
latch.await(); // 等待所有任务完成
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long endTime = System.currentTimeMillis();
System.out.println("创建并执行一百万个虚拟线程耗时: " + (endTime - startTime) + " ms");
}
}
执行结果:
可见,百万级任务执行只需要大概19s,相对于平台线程来说,极大提高了效率。

为了帮助更多像你一样的读者,我将持续在专栏中分享技术干货和实用技巧。如果你觉得这篇文章对你有帮助,可以考虑关注我的专栏,谢谢。