前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >每天一个Java面试题之虚拟线程

每天一个Java面试题之虚拟线程

作者头像
灬沙师弟
发布2024-08-20 20:26:20
760
发布2024-08-20 20:26:20
举报
文章被收录于专栏:Java面试教程

前言

虚拟线程是 Java 21 中最为重要的特性。Java 从 Java 19 开始引入虚拟线程,在 Java 21 中就正式升级为正式特性。可见官方也把虚拟线程作为 Java 21 长久支持版本的吸引点。虚拟线程是轻量级的线程,可以在显著的减少代码编写的同时提高系统的吞吐量。

一、引入虚拟线程原因

一直以来,在 Java 并发编程中,Thread 都是十分重要的一部分,Thread 是 Java 中的并发单元,每个 Thread 线程都提供了一个堆栈来存储局部变量和方法调用,以及线程上下文等相关信息。

但问题是线程和进程一样,都是一项昂贵的资源,JDK 将 Thread 线程实现为操作系统线程的包装器,成本很高,而且数量有限。因此我们会使用线程池来管理线程,同时限制线程的数量。比如常用的 Tomcat 会为每次请求单独使用一个线程进行请求处理,同时限制处理请求的线程数量以防止线程过多而崩溃;这很有可能在 CPU 或网络连接没有耗尽之前,线程数量已经耗尽,从而限制了 web 服务的吞吐量。

可能有些同学要说了,那么可以放弃请求和线程一一对应的方式,使用异步编程来解决这个问题。把请求处理分段,在组合成顺序管道,通过一套 API 进行管理,这样就可以使用有限的线程来处理超过线程数量的请求。这当然也是可以的,但是随之而来的问题是:

  • 需要额外的学习异步编程。
  • 代码复杂度增加,等于放弃了语言的基本顺序组合运算。
  • 堆栈上下文信息都变得难以追踪。
  • Debug 困难。
  • 与Java平台本身的编程风格有冲突,Java 并发单元是 Thread,而这时是异步管道。

而事实上,以上面的请求开启一个线程处理为例,因为 DB 查询速度过慢,请求量过大,可能导致我们的线程数量已经使用殆尽,新的请求将被阻塞,但是机器的性能尚有剩余剩余,性能浪费。

那么对于这种需要提高吞吐量的场景,使用虚拟线程将会大大改善这种情况。

二、虚拟线程的使用

这里我们不去介绍虚拟线程的实现原理,对开发者来说虚拟线程在使用体验上和 Thread 几乎没有区别,与之前的 API 互相兼容,但是相比之下虚拟线程资源占用非常少,虚拟线程是一种即用即启动的线程,不应该被池化存储。

三、创建提交执行虚拟线程

下面是一个示例,创建 1 万个线程,然后都休眠 1 秒钟结束线程,如果使用传统的 Thread 线程,可能会因为线程数量不够而直接异常。如果是线程池的方式,会基于线程池的线程数并发,那么剩余线程只能等待;但是使用虚拟线程的方式,可以瞬间完成。

代码语言:javascript
复制
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class VirtualThread {
    public static void main(String[] args) throws InterruptedException {
        // 创建并提交执行虚拟线程
        long start = System.currentTimeMillis();
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            IntStream.range(0, 10_000).forEach(i -> {
                executor.submit(() -> {
                    Thread.sleep(Duration.ofSeconds(1));
                    return i;
                });
            });
        }
        System.out.println("time:" + (System.currentTimeMillis() - start) + "ms");
    }
}
代码语言:javascript
复制
执行后发现 1.050 秒执行完毕,速度惊人。
$ java VirtualThread.java
time:1050ms

四、设置虚拟线程名称

代码语言:javascript
复制
Thread thread1 = Thread.ofVirtual().name("v-thread").unstarted(() -> {
    String threadName = Thread.currentThread().getName();
    System.out.println(String.format("[%s] Hello Virtual Thread", threadName));
});
代码语言:javascript
复制
thread1.start();
输出:[v-thread] Hello Virtual Thread

五、启动为虚拟线程

代码语言:javascript
复制
Thread thread2 = new Thread(() -> {
    String threadName = Thread.currentThread().getName();
    System.out.println(String.format("[%s] Hello Virtual Thread 2", threadName));
});
Thread.startVirtualThread(thread2);

六、判断是否是虚拟线程

最后,可以使用 isVirtual 方法判断一个线程对象是否是虚拟线程。

代码语言:javascript
复制
Thread thread1 = Thread.ofVirtual().name("v-thread").unstarted(() -> {
    String threadName = Thread.currentThread().getName();
    System.out.println(String.format("[%s] Hello Virtual Thread", threadName));
});
// 判断是否是虚拟线程
System.out.println(thread1.isVirtual());

小结

本文从概念上简单的讲解了一下Java的虚拟线程,并简单使用代码进行说明,希望能给你在面试与平时工作中产生帮助。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java面试教程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 一、引入虚拟线程原因
      • 二、虚拟线程的使用
        • 三、创建提交执行虚拟线程
          • 四、设置虚拟线程名称
            • 五、启动为虚拟线程
              • 六、判断是否是虚拟线程
              • 小结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档