【Java】虚拟线程详解

前言

虚拟线程在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,相对于平台线程来说,极大提高了效率。

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

相关推荐
2401_891482175 小时前
C++中的代理模式实战
开发语言·c++·算法
独断万古他化5 小时前
【抽奖系统开发实战】Spring Boot 抽奖模块全解析:MQ 异步处理、缓存信息、状态扭转与异常回滚
java·spring boot·redis·后端·缓存·rabbitmq·mvc
weisian1515 小时前
Java并发编程--12-读写锁与StampedLock:高并发读场景下的性能优化利器
java·开发语言·性能优化·读写锁·stampedlock
2401_838683375 小时前
C++中的代理模式高级应用
开发语言·c++·算法
暮冬-  Gentle°9 小时前
C++中的命令模式实战
开发语言·c++·算法
Volunteer Technology12 小时前
架构面试题(一)
开发语言·架构·php
清水白石00812 小时前
Python 对象序列化深度解析:pickle、JSON 与自定义协议的取舍之道
开发语言·python·json
2401_8769075212 小时前
Python机器学习实践指南
开发语言·python·机器学习
努力中的编程者12 小时前
栈和队列(C语言底层实现环形队列)
c语言·开发语言
回到原点的码农13 小时前
Spring Data JDBC 详解
java·数据库·spring