虚拟线程,多线程,单线程
什么是虚拟线程?
虚拟线程是 Java 中的一种轻量级线程,它旨在解决传统线程模型中的一些限制,提供了更高效的并发处理能力,允许创建数千甚至数万个虚拟线程,而无需占用大量操作系统资源。
为什么引用虚拟线程?
主要动机是为了克服传统“线程-每-请求”模型的可伸缩性限制。平台线程是重量级资源。创建过多平台线程会耗尽内存,并导致操作系统施加的高昂上下文切换成本,从而限制应用程序的吞吐量,尤其是在 I/O 密集型任务(线程大部分时间在等待网络、磁盘或数据库操作)中。引入虚拟线程是为了让应用程序能够高效处理极大数量的并发任务,同时避免强迫开发者采用复杂的异步编程模型(如响应式流或 CompletableFuture
链),从而保持更简单、更熟悉的同步阻塞式编程风格。其目标是实现高吞吐量和高开发者生产力。
虚拟线程和多线程单线程之间的对比
对比维度 | 单线程 | 多线程(平台线程) | 虚拟线程 |
---|---|---|---|
管理主体 | 操作系统内核(进程内唯一线程) | 操作系统内核(独立调度) | 编程语言运行时(如 JVM、Go Runtime) |
资源消耗 | 低(仅一个线程资源),但并发能力受限 | 高(栈空间通常为 MB 级,独立寄存器等) | 极低(栈空间为 KB 级,可动态伸缩) |
最大并发数 | 1(同一时间仅一个任务) | 有限(通常数千级,受内存和调度限制) | 极高(百万级甚至更多,轻量且易创建) |
上下文切换成本 | 无(仅一个线程,无需切换) | 高(需进入内核态,涉及内核调度) | 低(用户态切换,不依赖内核) |
适用场景 | 简单任务、顺序性要求高的场景(如脚本) | CPU 密集型任务(如数据计算、图像处理) | I/O 密集型任务(如网络请求、数据库操作) |
编程复杂度 | 低(无需处理线程安全) | 高(需处理锁、同步、线程安全问题) | 低(接近同步编程模型,运行时自动调度) |
并行能力 | 无(无法利用多核 CPU) | 有(可在多核 CPU 上并行执行) | 间接利用(依赖平台线程实现并行) |
如何使用
JDK 21 提供了几种创建和使用虚拟线程的方法:
•
Thread.startVirtualThread(Runnable task)
: 一个简单的静态方法,用于立即创建并启动一个虚拟线程。•
Thread.ofVirtual()
: 返回一个Thread.Builder
,用于配置和创建虚拟线程(例如,设置名称)。•
Executors.newVirtualThreadPerTaskExecutor()
: 管理多个任务时推荐使用的方法。这个工厂方法返回一个ExecutorService
,它为每个提交的任务创建一个新的虚拟线程,避免了线程池化的需要(通常不鼓励对虚拟线程进行池化,因为它们创建成本极低)。\
使用 Thread.startVirtualThread
import java.time.Duration;publicclassSimpleVirtualThread {publicstaticvoidmain(String[] args)throws InterruptedException {System.out.println("主线程: " + Thread.currentThread());// 创建并启动一个虚拟线程Threadvt= Thread.startVirtualThread(() -> {System.out.println("在虚拟线程内部: " + Thread.currentThread());try {// 模拟一些工作或阻塞 I/OThread.sleep(Duration.ofSeconds(1));} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("虚拟线程结束。");});System.out.println("虚拟线程已启动: " + vt);// 等待虚拟线程结束 (可选)vt.join();System.out.println("主线程结束。");} }
使用Thread Builder
@Testpublic void virtualThreadTest() {Thread.ofVirtual().name("virtualThreadTest").uncaughtExceptionHandler((t,e)-> System.out.println("线程[" + t.getName() + "发生了异常。message:" + e.getMessage())).start(()->{System.out.println("这是死磕 Java 新特性的文章—Java 19 新特性:虚拟线程");});}
使用 Executors.newVirtualThreadPerTaskExecutor
(推荐方式)
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.IntStream;
import java.time.Duration;publicclassExecutorVirtualThreads {publicstaticvoidmain(String[] args) {// 创建一个为每个任务启动新虚拟线程的 ExecutorServicetry (ExecutorServiceexecutor= Executors.newVirtualThreadPerTaskExecutor()) {System.out.println("提交任务...");// 提交多个任务,每个任务都在其自己的新虚拟线程中运行IntStream.range(0, 5).forEach(i -> {executor.submit(() -> {System.out.println("任务 " + i + " 在线程中运行: " + Thread.currentThread());try {// 模拟阻塞工作Thread.sleep(Duration.ofMillis(500));} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("任务 " + i + " 完成。");});});System.out.println("任务已提交。执行器将在任务完成后关闭。");// try-with-resources 代码块确保 executor.close() 被调用,// 它会等待已提交的任务完成后再关闭。} // ExecutorService 在这里自动关闭System.out.println("主方法结束。");}
}
如何实现?
使用虚拟线程优化CompletableFuture
import java.util.concurrent.*;public class VirtualThreadDemo {public static void main(String[] args) {// 创建虚拟线程执行器Executor executor = Executors.newVirtualThreadPerTaskExecutor();// 异步任务示例CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {System.out.println("开始任务:" + Thread.currentThread().getName());try {Thread.sleep(1000); // 模拟耗时操作} catch (InterruptedException e) {e.printStackTrace();}System.out.println("任务完成!");}, executor);// 等待任务完成future.join();System.out.println("所有任务完成!");}
}
代码说明:
- 虚拟线程执行器:通过
newVirtualThreadPerTaskExecutor()
创建轻量级线程池。 - 异步任务优化:
CompletableFuture.runAsync
结合虚拟线程,避免传统线程池资源占用问题。 - 任务管理简单化:无需手动管理线程池,代码更加直观。