JDK21新特性之虚拟线程

背景

我们在Java中经常使用的线程叫做平台线程(Platform Threads)它与操作系统内核空间中的线程对应,它由操作系统进行管理,可创建最大数量受限于操作系统。

在Linux中可以通过cat /proc/sys/kernel/threads-max命令查看操作系统单个进程支持最大线程数量。

在高并发应用中,我们常使用多线程来提高程序处理效率,当线程创建数量达到瓶颈便无法处理更多的请求。线程的管理开销较高,需要CPU频繁切换时间片,而我们池化后也只能解决线程创建和销毁的开销,并不能创建更多的线程数量,也无法解决频繁切换线程开销的问题。

什么是虚拟线程?

虚拟线程(Virtual Threads)也就是其他语言中的协程。例如:Python中的coroutine,GoLang中的goroutine。

为什么要使用虚拟线程?

虚拟线程是以平台线程为载体的轻量级线程,它运行在用户空间内由JVM管理,占用空间更小,同时也能减少线程切换的开销,支持创建更多的虚拟线程,支持更多的并发量,提高性能。可以更有效的利用资源,降低运营成本。

如何创建一个虚拟线程?

方式一

public class Main {

    public static void main(String[] args) {

		// 创建一个平台线程
        Thread.ofPlatform().start(()->{
            System.out.println("pt="+Thread.currentThread());
        });

		// 创建一个虚拟线程
        Thread vt = Thread.ofVirtual().start(() -> {
            System.out.println("vt="+Thread.currentThread());
        });

        // 等待虚拟线程执行完毕,再退出主程序
        vt.join();
        //或使用效果一样 new CountDownLatch(1).await(2, TimeUnit.SECONDS);
    }
}

运行结果:

pt=Thread[#21,Thread-0,5,main]
vt=VirtualThread[#22]/runnable@ForkJoinPool-1-worker-1
vt2=VirtualThread[#24]/runnable@ForkJoinPool-1-worker-2

其中虚拟线程 VirtualThread[#22] 运行在平台线程worker-1中,worker-1ForkJoinPool-1线程池中的线程。

方式二:通过线程池的方式创建虚拟线程

      @Test
    public void testVtExecutor() throws InterruptedException {
        var max = 1_000_000;
        // 创建一个计时器
        CountDownLatch latch = new CountDownLatch(max);
        // 统计平台线程
        Set<String> ptNames = ConcurrentHashMap.newKeySet();
        // 虚拟线程池
        ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
        // 创建100万个虚拟线程
        IntStream.range(0, max).forEach(i ->{
            executor.submit(() -> {
                // 计时器减1
                latch.countDown();
                // 每个虚拟线程 睡眠1秒并统计平台线程名称
                Thread.sleep(Duration.ofSeconds(1));
                ptNames.add(Thread.currentThread().toString().split("@")[1]);// VirtualThread[#9297]/runnable@ForkJoinPool-1-worker-12
                return i;// callable抛出异常
            });
        });
        // 等待统计结果
        latch.await();
        // 打印
        System.out.println("ptNames.size=" + ptNames.size());
    }

运行结果:

ptNames.size=12

可以看到100万个虚拟线程只用了12个平台线程,极大减少了线程创建数量。

虚拟线程原理

一个平台线程对应多个虚拟线程,但不是一一绑定的,每个平台线程会维护一个任务列表,并从此列表获取虚拟线程。

  1. 当虚拟线程启动时,它会绑定到某个平台线程,平台线程作为载体运行在线程池中。
  2. 当虚拟线程阻塞时,它会从平台线程中卸载。这个平台线程就可以运行其他虚拟线程。甚至去其他平台线程中获取任务。
  3. 当虚拟线程恢复到非阻塞时,它会加载到某个平台线程的任务列表中。

这样我们就可以利用少量的平台线程,来运行大量的虚拟线程,从而节省平台线程资源。

虚拟线程的使用场景

  1. 虚拟线程更适合处理阻塞式任务,在阻塞期间将CPU资源让给其他任务来提高效率。
  2. 不适合执行CPU密集型或非阻塞任务,虚拟线程不会运行的更快,只会提高规模。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值