一、虚拟线程

      虚拟程是Java语言中实现的一种轻量级线程,由Java虚拟机(JVM)管理而非操作系统,其核心优势在于高并发场景下的资源效率和性能提升‌。 旨在解决传统操作系统线程资源占用大、调度开销高的问题。

二、虚拟线程和传统线程相比,优势非常明显,主要体现在三个方面:

1. 资源占用极低,支持百万级并发
传统线程每个需要占用 1MB 左右的栈空间,一旦创建几千个,服务器内存就容易被耗尽;而虚拟线程初始栈空间仅 100-200KB,一次创建 100 万个都不成问题。打个比方,传统线程像 “大卡车”,一次拉不了几箱货;虚拟线程像 “小电动车”,一次能跑几百辆,运输效率直接拉满。

2. 无需手动调参,JVM 自动适配负载
以前用线程池,得反复纠结 “核心线程数设多少?最大线程数要不要增加?任务队列容量够不够?”,参数调不好就会出现线程溢出或资源浪费。虚拟线程完全不用操心这些,JVM 会根据任务负载自动调度,开发者只需提交任务,剩下的交给 JVM 就行,省心又高效。

3. IO 阻塞不占 CPU,资源利用率大幅提升
传统线程遇到 IO 操作(比如调用接口、查询数据库)时,会一直 “霸占” CPU 等待结果,导致 CPU 空转;而虚拟线程在 IO 阻塞时,会主动释放 CPU 资源,让其他任务继续执行,等 IO 操作有结果后再重新获取 CPU。这样一来,CPU 利用率能从原来的 50% 左右提升到 90% 以上,服务器性能充分释放。

三、虚拟线程VS传统线程

传统 Java 线程就像一辆辆大巴车(重量级,启动成本高)
而虚拟线程更像很多小电动车(数量多,快速启停,占用内存低)

四、四种创建虚拟线程(Java 21+)

方案一:使用Thread.startVirtualThread()方法,方法内部会自动开启线程

  // 方式1:快速启动虚拟线程
       Thread th= Thread.startVirtualThread(() -> {
            System.out.println(Thread.currentThread());
            System.out.println("startVirtualThread!");
        });


       //等待虚拟线程执行完毕
        try {
            th.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

方案二:使用Thread.ofVirtual()方法,创建虚拟线程

 //方式2:Builder模式
       Thread th= Thread.ofVirtual().name("virtualThread-1").start(() -> {
           System.out.println(Thread.currentThread());
            System.out.println("ofVirtual!");
        });

       //等待虚拟线程执行完毕
        try {
            th.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

方案三:通过Thread.ofVirtual).factory0方法获取一个线程工厂,再通过线程工厂创建虚拟线程

     // 方式3:工厂模式
        ThreadFactory factory = Thread.ofVirtual().factory();
        // 创建虚拟线程
        Thread th = factory.newThread(() -> {
            System.out.println(Thread.currentThread());
            System.out.println("ThreadFactory!");
        });

        // 启动虚拟线程
        th.start();
       //等待虚拟线程执行完毕
        try {
            th.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

方案四:使用Executors.newVirtualThreadPerTaskExecutor()方法,创建虚拟线程(推荐

 // 方式4:使用ExecutorService(推荐!)
        ExecutorService executor= Executors.newVirtualThreadPerTaskExecutor();
        //每次提交一个任务,都会创建一个虚拟线程
        Future<?> future1=  executor.submit(() -> {
            System.out.println(Thread.currentThread());
            System.out.println("Runnable任务执行完成!");
        });

        // 提交Callable任务
        Future<String> future2 = executor.submit(() -> {
            Thread.sleep(2000);
            return "Callable任务返回值";
        });

        // 获取Callable任务结果
        try {
            String result = future2.get();
            System.out.println("任务结果: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        // 关闭线程池
        executor.shutdown();

五、使用虚拟线程Java实现网络通信

(1)  服务器端

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
    public static void main(String[] args) {
        int port=8888;
        try {
            //创建服务器端Socket,绑定到指定端口上
            ServerSocket server=new ServerSocket(port);
            System.out.println("服务器启动,等待客户端连接...");
            while (true){
                //阻塞等待客户端连接
                Socket socket=server.accept();
                //启动一个线程,等待客户端连接
               Thread.ofVirtual().start(() -> {
                   //通过socket获取输入流和输出流
                   try( PrintWriter out=new PrintWriter(socket.getOutputStream(),true);
                        BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));) {
                     String line;
                     //循环读取客户端发送的消息
                     while((line=in.readLine())!=null){
                         //获取当前线程信息,并将其转换为字符串
                         String  thInfo=Thread.currentThread().toString();
                         //将客户端发送的消息打印到控制台,并加上线程信息
                         out.println(thInfo+":"+line);
                     }
                   }catch (Exception e){
                       e.printStackTrace();
                   }
               });
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

(2)  客户端

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class MyClient {
    public static void main(String[] args) {
        try {
            //创建客户端Socket,与服务端建立连接
            Socket socket=new Socket("localhost",8888);
            //创建输入输出流对象
            BufferedReader stdin=new BufferedReader(new InputStreamReader(System.in));
            PrintWriter out=new PrintWriter(socket.getOutputStream(),true);
            BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));

            String cline;
            //循环读取控制台输入,并将其发送给服务端
            while((cline=stdin.readLine())!=null){
                out.println(cline);
                //读取服务端返回的消息,并打印到控制台
                System.out.println(in.readLine());
                if("bye".equals(cline)){
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

(3) 启动服务端

(4) 启动客户端2个,输入bye就结束!

六、适用场景

  1. 虚拟线程适用于执行阻塞式任务,在阻塞期间,可以将CPU资源让渡给其他任务
  2. 虚拟线程不适合CPU密集计算或非阻塞任务,虚拟线程并不会运行的更快,而是增加了规模
  3. 虚拟线程是轻量级资源,用完即抛,不需要池化
  4. 通常我们不需要直接使用虚拟线程,像Tomcat、Jetty、Netty、Springboot等都已支持虚拟线程
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐