Java21的虚拟线程Virtual Thread初体验

java21,virtual,thread · 浏览次数 : 111

正文

我们之前使用的是操作系统平台的线程,就称之为“系统线程”吧。虚拟线程是JDK维护的,原理跟WebFlux的底层实现差不多,都是工作线程分离。

要使用虚拟线程,需要使用JDK21以上,包括21。

虚拟线程可以创建很多很多

系统线程不能轻易创建太多,我们一直被教导创建线程是很重的活动。

        for (int i = 0; i < 1_000_000; i++) {
            new Thread(() -> {
                longAdder.increment();
                System.out.println(longAdder.longValue());
                try {
                    Thread.sleep(10000);
                } catch (Exception e) {
                    // deal with e
                }
            }).start();
        }

上面尝试创建百万个线程,线程都会休眠不结束。我用了一个LongAdder记录我的笔记本能实际创建多少线程。结果是4000多个,用了6秒:
image
改成虚拟线程就轻松成功:

        LongAdder longAdder = new LongAdder();
        for (int i = 0; i < 1_000_000; i++) {
            Thread.ofVirtual().start(() -> {
                longAdder.increment();
                System.out.println(longAdder.longValue());
                try {
                    Thread.sleep(100000);
                } catch (Exception e) {
                    // deal with e
                }
            });
        }

因为虚拟线程很轻量,所以不要使用线程池,可以很轻易的创建很多个。因为能创建很多,所以也不要使用 Thread Local 变量。

IO操作不好阻塞虚拟线程的使用

使用系统线程,必须通过线程池来处理多个任务,不然问题很严重:

    static void callService(String taskName) {
        try {
            System.out.println(Thread.currentThread() + " executing " + taskName);
            new URL("自己写一个http接口?sleep=2000").getContent();
            System.out.println(Thread.currentThread() + " completed " + taskName);

        } catch (Exception e) {
            // deal with e
        }
    }
	
try (ExecutorService executor = Executors.newFixedThreadPool(5)) {
	for (int i = 0; i <= 10; i++) {
		String taskName = "Task" + i;
		executor.execute(() -> callService(taskName));
	}
}

执行的时候你能看到,线程在执行结束前需要空闲等待任务的IO。毕竟每个任务都是在某一个线程上执行 —— 说这个干啥?
看一下虚拟线程

        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i <= 600; i++) {
                String taskName = "Task" + i;
                executor.execute(() -> callService(taskName));
            }
        }

这里创建了一个虚拟线程工厂(而不是线程池,记住不要使用虚拟化的线程池),它会给每个任务创建新的虚拟线程。
程序启动后,会立即打印600个"executing",而不是像系统线程那样只打印5个。
为了方便,我们少用几个任务来实验看一下输出:

VirtualThread[#50]/runnable@ForkJoinPool-1-worker-2 executing Task1
VirtualThread[#48]/runnable@ForkJoinPool-1-worker-1 executing Task0
VirtualThread[#51]/runnable@ForkJoinPool-1-worker-3 executing Task2
VirtualThread[#51]/runnable@ForkJoinPool-1-worker-2 completed Task2
VirtualThread[#48]/runnable@ForkJoinPool-1-worker-3 completed Task0
VirtualThread[#50]/runnable@ForkJoinPool-1-worker-1 completed Task1

仔细看,这里一共3个虚拟线程,因为工厂创建了三个,根据任务数来的。
但是每个任务都是在两个虚拟线程上:Task1 被worker-2接收,却被worker-1完成。

啥时候用

关键问题来了,我们总该使用虚拟线程吗?
对各种问题都通用的答案是:你没遇到问题就别想着解决问题。
如果的确有问题,想看看虚拟线程是否合适,可以看一下任务是否是IO密集型的。
对于计算密集型任务,系统线程比虚拟线程有效得多。
虚拟线程跟WebFlux一样,只能提升系统的吞吐量,并不能加快单个任务的完成时间。

与Java21的虚拟线程Virtual Thread初体验相似的内容:

Java21的虚拟线程Virtual Thread初体验

我们之前使用的是操作系统平台的线程,就称之为“系统线程”吧。虚拟线程是JDK维护的,原理跟WebFlux的底层实现差不多,都是工作线程分离。 要使用虚拟线程,需要使用JDK21以上,包括21。 虚拟线程可以创建很多很多 系统线程不能轻易创建太多,我们一直被教导创建线程是很重的活动。 for (int

Java 21 正式 GA,虚拟线程真的来了

UTC 时间 2023 年 9 月 19 日,期盼已久的 Java 21 终于发布正式版! 本文一起来看看其中最受 Java 开发者关注的一项新特性:Loom 项目的两个新特性之一的 ”虚拟线程(Virtual Thread)“(另外一个新特性是 ”结构化并发(Structured Concurre

Java 21 新特性:虚拟线程(Virtual Threads)

在Java 21中,引入了虚拟线程(Virtual Threads)来简化和增强并发性,这使得在Java中编程并发程序更容易、更高效。 虚拟线程,也称为“用户模式线程(user-mode threads)”或“纤程(fibers)”。该功能旨在简化并发编程并提供更好的可扩展性。虚拟线程是轻量级的,这

一次惨痛的面试:“网易提前批,我被虚拟线程问倒了”

一、写在开头 昨晚收到一个粉丝在私信的留言如下: build哥,今天参加了网易的提前批,可以说是一次惨痛的面试体验,直接被虚拟线程问倒了,无论是在校学习的时候还是在公司实习的时候,都使用的是Java8更多,或者Java11,比较点子背的是面试我的这一个面试官,他们团队刚好在做Java21的切换,

Java21上手体验-分代ZGC和虚拟线程

一、导语 几天前Oracle刚刚发布了Java21, 由于这是最新的LTS版本,引起了大家的关注。 我也第一时间在个人项目中进行了升级体验。 一探究竟,和大家分享。 二、Java21更新内容介绍 官方release公告: https://jdk.java.net/21/release-notes 开

Java 21新特性-虚拟线程 审核中

Java 21 版本更新中最重要的功能之一就是虚拟线程 (JEP 444)。这些轻量级线程减少了编写、维护和观察高吞吐量并发应用程序所需的工作量。

Java 21的StringBuilder和StringBuffer新增了一个repeat方法

发现Java 21的StringBuilder和StringBuffer中多了repeat方法: /** * @throws IllegalArgumentException {@inheritDoc} * * @since 21 */ @Override public StringBuilder

Java 21 新特性:switch的模式匹配

在之前的Java 17新特性中,我们介绍过关于JEP 406: switch的模式匹配,但当时还只是关于此内容的首个预览版本。之后在JDK 18、JDK 19、JDK 20中又都进行了更新和完善。如今,在JDK 21中,该特性得到了最终确定!下面,我们就再正式学习一下该功能! 在以往的switch语

Java 21新特性:Sequenced Collections(有序集合)

在JDK 21中,Sequenced Collections的引入带来了新的接口和方法来简化集合处理。此增强功能旨在解决访问Java中各种集合类型的第一个和最后一个元素需要非统一且麻烦处理场景。 下面一起通过本文来了解一下不同集合处理示例。 Sequenced Collections接口 Seque

前瞻|Java 21 新特性 String Templates(字符串模版)

在日常写Java的时候,对于字符串的操作是非常普遍的,其中最常见的就是对字符串的组织。也因为这个操作非常普遍,所以诞生了很多方案,总下来大概有这么几种: - 使用`+`拼接 - 使用`StringBuffer`和`SpringBuilder` - `String::format` and `Stri