Java 17 虚拟线程与线程池混合使用方案
下文笔者讲述java17中虚拟线程(Virtual Threads)与传统的线程池结合使用的简介说明,如下所示
虚拟线程与线程池的简介说明
虚拟线程(JVM 层面的轻量级线程)适合处理IO 密集型、
阻塞等待的任务,
而线程池(平台线程池)适合管理资源、控制并发度。
混合使用的核心思路是:
1. 用线程池管理平台线程(载体),
由平台线程来启动虚拟线程;
2. 虚拟线程执行具体的阻塞/IO 任务,
避免平台线程被阻塞,提升整体吞吐量;
3. 区分任务类型:
CPU密集型任务仍用传统线程池
IO密集型任务通过线程池调度虚拟线程执行。
方案1:线程池+虚拟线程工厂
使用`Executors.defaultThreadFactory()` 或自定义工厂,
让线程池的平台线程作为“调度器”,
启动虚拟线程执行任务
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class VirtualThreadWithThreadPool {
public static void main(String[] args) throws InterruptedException {
// 1. 创建传统平台线程池(核心作用:控制并发调度的平台线程数)
// 适合IO密集型场景:核心线程数可设为CPU核心数,避免平台线程过多竞争
ExecutorService platformThreadPool = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors() // CPU核心数,如8核设为8
);
// 2. 提交1000个IO密集型任务,由线程池的平台线程启动虚拟线程执行
for (int i = 0; i < 1000; i++) {
int taskId = i;
platformThreadPool.submit(() -> {
// 3. 启动虚拟线程执行具体的阻塞任务
Thread.startVirtualThread(() -> {
try {
// 模拟IO阻塞(如数据库查询、网络请求)
TimeUnit.MILLISECONDS.sleep(100);
System.out.printf("虚拟线程执行任务%d,载体平台线程:%s%n",
taskId, Thread.currentThread().getCarrierThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
});
});
}
// 4. 优雅关闭线程池
platformThreadPool.shutdown();
if (!platformThreadPool.awaitTermination(10, TimeUnit.SECONDS)) {
platformThreadPool.shutdownNow();
}
}
}
方案2:自定义虚拟线程池(JDK 19+预览,17需手动封装)
Java 17 中虚拟线程没有官方的“虚拟线程池"
但可封装 `Executors.newVirtualThreadPerTaskExecutor()`
结合线程池的拒绝策略、生命周期管理:
import java.util.concurrent.;
public class CustomVirtualThreadPool {
// 核心:用平台线程池做调度,虚拟线程执行任务
private final ExecutorService platformExecutor;
// 虚拟线程执行器(每个任务一个虚拟线程)
private final ExecutorService virtualExecutor;
public CustomVirtualThreadPool(int corePlatformThreads) {
// 1. 平台线程池:控制调度并发度
this.platformExecutor = new ThreadPoolExecutor(
corePlatformThreads,
corePlatformThreads,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
// 平台线程工厂(命名便于排查)
new ThreadFactory() {
private int count = 0;
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("platform-scheduler-" + count++);
return t;
}
}
);
// 2. 虚拟线程执行器(Java 17+支持)
this.virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();
}
// 提交任务:由平台线程调度,虚拟线程执行
public <T> Future<T> submit(Callable<T> task) {
return platformExecutor.submit(() -> {
// 用虚拟线程执行具体任务
return virtualExecutor.submit(task).get();
});
}
// 优雅关闭
public void shutdown() throws InterruptedException {
virtualExecutor.shutdown();
platformExecutor.shutdown();
if (!platformExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
platformExecutor.shutdownNow();
virtualExecutor.shutdownNow();
}
}
// 测试
public static void main(String[] args) throws InterruptedException, ExecutionException {
CustomVirtualThreadPool pool = new CustomVirtualThreadPool(Runtime.getRuntime().availableProcessors());
// 提交100个IO任务
for (int i = 0; i < 100; i++) {
int taskId = i;
Future<String> future = pool.submit(() -> {
TimeUnit.MILLISECONDS.sleep(50); // 模拟IO阻塞
return "任务" + taskId + "完成,虚拟线程:" + Thread.currentThread().getName();
});
System.out.println(future.get());
}
pool.shutdown();
}
}
注意事项
1. 任务类型区分:
- CPU 密集型任务(如计算、循环):
直接用传统线程池(平台线程),虚拟线程无优势;
- IO 密集型任务(如网络请求、数据库操作):
用“线程池+虚拟线程”,最大化吞吐量。
2. 资源控制:
- 平台线程池的核心线程数建议设为 `CPU核心数`(如 8 核设 8)
避免平台线程过多导致上下文切换;
- 虚拟线程数量无上限(JVM 自动管理),无需限制。
3. 异常处理:
虚拟线程的异常不会自动打印,需在任务内捕获,
或通过 `Future.get()` 获取异常。
4. 兼容性:
Java 17 中虚拟线程是预览特性(需启动参数 `--enable-preview`),
Java 21 中正式转正,生产环境建议升级到 Java 21 或确保启动参数正确。
总结
1. 核心方案:用传统平台线程池控制并发调度度,
由平台线程启动虚拟线程执行 IO 密集型阻塞任务,
兼顾资源管理和轻量级优势;
2. 关键原则:CPU 密集型用平台线程池,
IO 密集型用“平台线程池+虚拟线程”;
3. 实践要点:平台线程池核心数设为 CPU 核心数,
做好虚拟线程的异常捕获和线程池优雅关闭。
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。


