虚拟线程(Virtual Thread)
1.什么是虚拟线程?
虚拟线程是一种轻量级的线程,旨在解决传统资源占用较大、调度开销高的问题。
传统线程是由操作系统进行调度和管理的,而虚拟线程的调度由 Java 虚拟机(JVM)管理。虚拟线程并不由操作系统调度,而是由普通线程调度。
一个操作系统可以调度成百上千个虚拟线程。
1.1虚拟线程的特点:
- 轻量级资源:虚拟线程比传统的线程占用更少的资源
- 高效调度:虚拟线程可以由少数操作系统线程调度,大大提高了多任务处理的效率
- 自动挂起:当虚拟线程进行 IO操作时,他会自动挂起并让其它虚拟线程继续执行,直到 IO操作完成,虚拟线程才会恢复执行
- 现有代码兼容:无缝替代传统线程
2.虚拟线程的使用方式:
2.1直接创建虚拟线程并立即执行
//1.创建虚拟线程并立即运行Thread thread1 = Thread.startVirtualThread(()->{System.out.println("Start virtual thread...");try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("End virtual thread...");});
2.2创建虚拟线程,并不立即执行
//2.创建虚拟线程,并不立即执行Thread thread2 = Thread.ofVirtual().unstarted(()->{System.out.println("Start virtual thread...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("End virtual thread...");});//手动启动线程thread2.start();
2.3通过 ThreadFactory 创建虚拟线程
//3.通过 ThreadFactory 创建虚拟线程ThreadFactory threadFactory = Thread.ofVirtual().factory();Thread thread3 = threadFactory.newThread(() -> {System.out.println("Start virtual thread...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("End virtual thread...");});//启动虚拟线程thread3.start();
2.4使用 Executor 服务来调度虚拟线程
//4.使用 Executor 服务来调度虚拟线程ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();//创建大量虚拟线程并提交到 Executor 中for (int i = 0; i < 10000; i++) {executorService.submit(()->{try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("End virtual thread");return true;});}
3.虚拟线程的性能对比:
3.1编写程序:
public class VirtualThread {public static void main(String[] args) throws InterruptedException{//使用虚拟线程测试:long virtualTime = virtualThreadTest();System.out.println("虚拟线程池执行耗时:" + virtualTime);//使用普通线程进行测试:long traditionalTime = traditionalThreadTest();System.out.println("普通线程池耗时:" + traditionalTime);// 计算两种线程执行时间的差异long timeDifference = traditionalTime - virtualTime;// 输出性能差异和速度提升倍数System.out.println("\n性能差异:" + formatTime(timeDifference) +" (" + String.format("%.2f", (double)traditionalTime / virtualTime) + " 倍速度提升)");}//普通线程执行器private static long traditionalThreadTest() throws InterruptedException {// 记录开始时间long start = System.currentTimeMillis();// 创建一个固定大小为100的线程池try (ExecutorService executor = Executors.newFixedThreadPool(100)) {// 提交taskCount个任务到线程池for (int i = 0; i < 10000; i++) {executor.submit(VirtualThread::threadSleep);}// 关闭线程池,不再接受新任务executor.shutdown();// 等待所有任务完成,最多等待1小时executor.awaitTermination(1, TimeUnit.HOURS);}// 计算并返回总耗时return System.currentTimeMillis() - start;}//虚拟线程执行器public static long virtualThreadTest(){long start = System.currentTimeMillis();//创建一个虚拟线程执行器try (ExecutorService es = Executors.newVirtualThreadPerTaskExecutor()){for (int i = 0; i < 10000; i++) {es.submit(VirtualThread::threadSleep);}//关闭线程池es.shutdown();//等待所有任务完成,最长为 1 HOURSes.awaitTermination(1, TimeUnit.HOURS);} catch (InterruptedException e) {throw new RuntimeException(e);}return System.currentTimeMillis() - start;}public static void threadSleep(){try {Thread.sleep(1000);} catch (InterruptedException e) {//重新设置中断状态Thread.currentThread().interrupt();}}// 格式化时间的方法,将毫秒转换为更易读的格式private static String formatTime(long milliseconds) {// 返回格式化的字符串,同时显示毫秒数和秒数(保留两位小数)return String.format("%d 毫秒 (%.2f 秒)", milliseconds, milliseconds / 1000.0);}
}
3.2测试结果:
3.3结果分析:
- 执行时间:
- 传统线程:约1分40秒
- 虚拟线程:约1.99秒
- 性能提升:
- 虚拟线程比传统线程快约50.44倍
- 并发处理:
- 传统线程池限制为100个线程,导致任务需要分批处理
- 虚拟线程能够同时处理所有 10000个任务,显著减少了总执行时间
- 资源利用:
- 虚拟线程更有效地利用了系统资源,特别是在 I/O 密集型任务时
总结:
- 显著的性能优势:在这个 I/O 密集型的测试场景中,虚拟线程比传统线程执行速度提升了 50 倍
- 适用于高并发场景:虚拟线程特别适合处理大并发任务而不会耗尽系统资源
- 资源效率:虚拟线程能更有效地利用系统资源,允许创建大量并发任务而不会耗尽系统资源