• Java SE 19 虚拟线程


    Java SE 19 虚拟线程

    作者:Grey

    原文地址:

    博客园:Java SE 19 虚拟线程

    CSDN:Java SE 19 虚拟线程

    说明#

    虚拟线程(Virtual Threads)是在Project Loom中开发的,并从 Java SE 19 开始作为预览功能引入 JDK。

    在线程模型下,一个 Java 线程相当于一个操作系统线程,而这些线程是很消耗资源的,如果启动的线程过多,会给整个系统的稳定性带来风险。

    虚拟线程解决了这个问题,从 Java 代码的角度来看,虚拟线程感觉就像普通的线程,但它们不是 1:1 地映射到操作系统线程上。

    有一个所谓的载体线程池,一个虚拟线程被临时映射到该池中。一旦虚拟线程遇到阻塞操作,该虚拟线程就会从载体线程中移除,而载体线程可以执行另一个虚拟线程(新的或之前被阻塞的)。

    所以阻塞的操作不再阻塞执行的线程。这使得我们可以用一个小的载体线程池来并行处理大量的请求。

    示例#

    场景

    启动 1000 个任务,每个任务等待一秒钟(模拟访问外部API),然后返回一个结果(在这个例子中是一个随机数)。

    任务类如下

    package git.snippets.vt;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ThreadLocalRandom;
    
    /**
     * @author Grey
     * @date 2022/9/21
     * @since 19
     */
    public class Task implements Callable {
    
        private final int number;
    
        public Task(int number) {
            this.number = number;
        }
    
        @Override
        public Integer call() {
            System.out.printf("Thread %s - Task %d waiting...%n", Thread.currentThread().getName(), number);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.printf("Thread %s - Task %d canceled.%n", Thread.currentThread().getName(), number);
                return -1;
            }
            System.out.printf("Thread %s - Task %d finished.%n", Thread.currentThread().getName(), number);
            return ThreadLocalRandom.current().nextInt(100);
        }
    }
    

    接下来,我们测试使用线程池开启 100 个线程处理 1000 个任务需要多长时间。

    package git.snippets.vt;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    /**
     * @author Grey
     * @date 2022/9/21
     * @since 19
     */
    public class App {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ExecutorService executor = Executors.newFixedThreadPool(100);
            List tasks = new ArrayList<>();
            for (int i = 0; i < 1_000; i++) {
                tasks.add(new Task(i));
            }
            long time = System.currentTimeMillis();
            List> futures = executor.invokeAll(tasks);
            long sum = 0;
            for (Future future : futures) {
                sum += future.get();
            }
            time = System.currentTimeMillis() - time;
            System.out.println("sum = " + sum + "; time = " + time + " ms");
            executor.shutdown();
        }
    }
    
    

    运行结果如下

    Thread pool-1-thread-1 - Task 0 waiting...
    Thread pool-1-thread-3 - Task 2 waiting...
    Thread pool-1-thread-2 - Task 1 waiting...
    ……
    sum = 49879; time = 10142 ms
    

    接下来,我们用虚拟线程测试整个事情。因此,我们只需要替换这一行

    ExecutorService executor = Executors.newFixedThreadPool(100);
    

    替换为

    ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
    

    执行效果如下

    Thread  - Task 0 waiting...
    Thread  - Task 2 waiting...
    Thread  - Task 3 waiting...
    ……
    sum = 48348; time = 1125 ms
    

    1125 ms VS 10142 ms,性能提升非常明显。

    注:本示例需要在 JDK 19 下运行,且需要增加 --enable-preview 参数,在 IDEA 下,这个参数配置如下

    image

    image

    我们已经了解了创建虚拟线程的一种方法:使用Executors.newVirtualThreadPerTaskExecutor()创建的执行器服务,为每个任务创建一个新的虚拟线程。

    使用Thread.startVirtualThread()Thread.ofVirtual().start(),我们也可以明确地启动虚拟线程。

    Thread.startVirtualThread(() -> {
      // code to run in thread
    });
    
    Thread.ofVirtual().start(() -> {
      // code to run in thread
    });
    

    特别说明:Thread.ofVirtual()返回一个VirtualThreadBuilder,其start()方法启动一个虚拟线程。另一个方法Thread.ofPlatform()返回一个PlatformThreadBuilder,通过它我们可以启动一个平台线程。

    这两种构造方法都实现了Thread.Builder接口。这使得我们可以编写灵活的代码,在运行时决定它应该在虚拟线程还是平台线程中运行。

    源码#

    hello-virtual-thread

    参考资料#

    Java 19 Features (with Examples)

    JDK 19 Release Notes

    Virtual Threads in Java (Project Loom)

  • 相关阅读:
    react-redux使用
    针对 jQuery 的优化方法有哪些
    7. 吴恩达深度学习--搭建循环神经网络及其应用
    为什么建议将常量用const关键字来修饰
    【预测模型-DELM分类】基于风驱动算法改进深度学习极限学习机实现数据分类附matlab代码
    Day20_网络编程(软件结构,网络编程三要素,UDP网络编程,TCP网络编程)
    意大利法院判定开源协议条款具有可强制执行性;一个命令即可安装 CSS 框架;LibreOffice 7.3 RC1 发布 | 开源日报
    顺序表第三节(通讯录基础版)
    Qt项目天气预报(1) - ui界面搭建
    亚微米像素技术发展
  • 原文地址:https://www.cnblogs.com/greyzeng/p/16732227.html