多线程回答的滚瓜烂熟,面试官问我虚线程了解吗?我说不太了解!

Java虚拟线程(Virtual Threads)标志着Java在并发编程领域的一次重大飞跃,特别是从Java 21版本开始。这项新技术的引入旨在克服传统多线程和线程池存在的挑战。

多线程和线程池

在Java中,传统的多线程编程依赖于Thread类或实现Runnable接口。这些线程都是重量级的,因为每个线程都对应一个操作系统级的线程,这意味着线程的创建、调度和销毁都需要操作系统的深度参与,不仅耗费资源,也消耗时间。

图片

为了优化资源使用和提高效率,Java提供了线程池(ExecutorService等)。线程池可以重用固定数量的线程,避免了频繁创建和销毁线程的开销。然而,即使是线程池也无法完全解决上下文切换和资源消耗的问题,尤其是在高并发场景下。此外,大量的线程创建还可能导致OutOfMemoryError

下面是一个线程池OutOfMemoryError的例子:

public static void main(String[] args) {
    stackOverFlowErrorExample();
}

private static void stackOverFlowErrorExample() {
    for (int i = 0; i < 100_000; i++) {
        new Thread(() -> {
            try {
                Thread.sleep(Duration.ofSeconds(1L));
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }).start();
    }
}

图片

虚拟线程引入

为了进一步提高并发编程的效率和简化开发过程,Java19引入了虚拟线程概念。这些轻量级的线程在JVM的用户模式下被管理,而不是直接映射到操作系统的线程上。这种设计使得可以创建数百万个虚拟线程,而对操作系统资源的消耗微乎其微。

当代码调用到阻塞操作时例如 IO、同步、Sleep等操作时,JVM 会自动把 Virtual Thread 从平台线程上卸载,平台线程就会去处理下一个虚拟线程,通过这种方式,提升了平台线程的利用率,让平台线程不再阻塞在等待上,从底层实现了少量平台线程就可以处理大量请求,提高了服务吞吐和 CPU 的利用率。

图片

  • 操作系统线程(OS Thread):由操作系统管理,是操作系统调度的基本单位。

  • 平台线程(Platform Thread):传统方式使用的Java.Lang.Thread,都是一个平台线程,是 Java 对操作系统线程的包装,与操作系统是 1:1 映射。

  • 虚拟线程(Virtual Thread):一种轻量级,由 JVM 管理的线程。对应的实例 java.lang.VirtualThread 这个类。

  • 载体线程(Carrier Thread):指真正负责执行虚拟线程中任务的平台线程。一个虚拟线程装载到一个平台线程之后,那么这个平台线程就被称为虚拟线程的载体线程。

使用虚拟线程

虚拟线程的使用接口与普通线程相似,但创建虚拟线程的方式略有不同。以下是几种创建和使用虚拟线程的方法:

  1. 直接创建虚拟线程并运行:

// 传入Runnable实例并立刻运行:
Thread vt = Thread.startVirtualThread(() -> {
    System.out.println("Start virtual thread...");
    Thread.sleep(10);
    System.out.println("End virtual thread.");
});
  1. 创建虚拟线程但不自动运行,而是手动调用start()开始运行:

// 创建VirtualThread:
Thread.ofVirtual().unstarted(() -> {
    System.out.println("Start virtual thread...");
    Thread.sleep(1000);
    System.out.println("End virtual thread.");
});
// 运行:
vt.start();
  1. 通过虚拟线程的ThreadFactory创建虚拟线程,然后手动调用start()开始运行:

// 创建ThreadFactory:
ThreadFactory tf = Thread.ofVirtual().factory();
// 创建VirtualThread:
Thread vt = tf.newThread(() -> {
    System.out.println("Start virtual thread...");
    Thread.sleep(1000);
    System.out.println("End virtual thread.");
});
// 运行:
vt.start();

直接调用start()实际上是由ForkJoinPool的线程来调度的。我们也可以自己创建调度线程,然后运行虚拟线程:

ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
// 创建大量虚拟线程并调度:
ThreadFactory tf = Thread.ofVirtual().factory();
for (int i=0; i<100_000; i++) {
    Thread vt = tf.newThread(() -> { ... });
    executor.submit(vt);
    executor.submit(() -> {
        System.out.println("Start virtual thread...");
        Thread.sleep(Duration.ofSeconds(1L));
        System.out.println("End virtual thread.");
        return true;
    });
}

由于虚拟线程属于非常轻量级的资源,因此,用时创建,用完就扔,不要池化虚拟线程。

虚线程的性能

下面我们测试一下虚线程的性能

public static void main(String[] args) {
    testWithVirtualThread();

    testWithThread(20);
    testWithThread(50);
    testWithThread(100);
    testWithThread(200);
    testWithThread(400);
}

private static long testWithVirtualThread() {
    long start = System.currentTimeMillis();
    ExecutorService es = Executors.newVirtualThreadPerTaskExecutor();
    for (int i = 0; i < TASK_NUM; i++) {
        es.submit(() -> {
            Thread.sleep(100);
            return 0;
        });
    }
    es.close();
    long end = System.currentTimeMillis();
    System.out.println("virtual thread:" + (end - start));
    return end;
}

private static void testWithThread(int threadNum) {
    long start = System.currentTimeMillis();
    ExecutorService es = Executors.newFixedThreadPool(threadNum);
    for (int i = 0; i < TASK_NUM; i++) {
        es.submit(() -> {
            Thread.sleep(100);
            return 0;
        });
    }
    es.close();
    System.out.println(threadNum + " thread:" + (System.currentTimeMillis() - start));
    es.shutdown();
}

下面是测试结果:

图片

虚线程真是快到飞起!!!

虚拟线程的原理

Java的虚拟线程会把任务(java.lang.Runnable实例)包装到一个 Continuation实例中。当任务需要阻塞挂起的时候,会调用Continuation 的 yield 操作进行阻塞,虚拟线程会从平台线程卸载。 当任务解除阻塞继续执行的时候,调用 Continuation.run会从阻塞点继续执行。下面让我们结合Thread.ofVirtual().start()来看一下虚线程的实现。

当调用start()方法时,会创建一个虚拟线程 var thread = newVirtualThread(scheduler, nextThreadName(), characteristics(), task);

 static Thread newVirtualThread(Executor scheduler,
                                   String name,
                                   int characteristics,
                                   Runnable task) {
        if (ContinuationSupport.isSupported()) {
            return new VirtualThread(scheduler, name, characteristics, task);
        } else {
            if (scheduler != null)
                throw new UnsupportedOperationException();
            return new BoundVirtualThread(name, characteristics, task);
        }
    }

核心主要在java.lang.VirtualThread类中。下面是JVM 调用VirtualThread的构造函数:

图片

VirtualThread 会初始化一个ForkJoinPool的Executor.

private static final ForkJoinPool DEFAULT_SCHEDULER = createDefaultScheduler(); 该方法初始化Executor线程池大小。该Executor 也就是执行器,提供了一个默认的 FIFO 的 ForkJoinPool 用于执行虚拟线程任务。

图片

之后创建一个VThreadContinuation对象。该对象存储作为Runnable对象运行的信息,它确保了每个并发操作都有清晰定义的生命周期和上下文。

图片

VThreadContinuation是一种允许程序执行被暂停并在将来某个时刻恢复的机制。虚拟线程利用VThreadContinuation来实现轻量级的上下文切换.

最后,该方法调用runContinuation方法。该方法在虚拟线程启动时被调用。

图片

JVM 把虚拟线程分配给平台线程的操作称为 mount(挂载),取消分配平台线程的操作称为 unmount(卸载)。

Continuation 组件十分重要,它既是用户真实任务的包装器,同时提供了虚拟线程任务暂停/继续的能力,以及虚拟线程与平台线程数据转移功能,当任务需要阻塞挂起的时候,调用 Continuation 的 yield 操作进行阻塞。当任务需要解除阻塞继续执行的时候,则调用 Continuation 的 run 恢复执行。

图片

总结

虚拟线程是由 Java 虚拟机调度,它的占用空间小,同时使用轻量级的任务队列来调度虚拟线程,避免了线程间基于内核的上下文切换开销,因此可以极大量地创建和使用。主要有以下好处:

  1. 虚拟线程是轻量级的,它们不直接映射到操作系统的线程,而是由JVM在用户态进行管理。这种轻量级特性允许在单个JVM实例中同时运行数百万个虚拟线程。

  2. 虚拟线程大大简化了并发编程的复杂性。开发者可以像编写顺序代码一样编写并发代码,而无需担心传统线程编程中的许多复杂问题,如线程数、同步和资源竞争等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/535456.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Green Hills 自带的MULTI调试器查看R7芯片寄存器

Green Hills在查看芯片寄存器时需要导入 .grd文件。下面以R7为例&#xff0c;演示一下过程。 首先打开MULTI调试器&#xff0c;如下所示View->Registers&#xff1a; 进入如下界面&#xff0c;选择导入寄存器定义文件.grd&#xff1a; 以当前R7芯片举例&#xff08;dr7f7013…

室内定位中文综述阅读

1 室内高精度定位技术总结与展望 [4]柳景斌,赵智博,胡宁松等.室内高精度定位技术总结与展望[J].武汉大学学报(信息科学 版),2022,47(07):997-1008.DOI:10.13203/j.whugis20220029. 1.1.1 WiFi‐RTT定位 2016 年 12 月&#xff0c;随着新版 IEEE802.11 标准的公布&#xff0c…

力扣55. 跳跃游戏

Problem: 55. 跳跃游戏 文章目录 题目描述思路复杂度Code 题目描述 思路 将题目稍作转化&#xff1a;验证最远走到的距离是否超出组数&#xff1b; 1.获取数组nums的长度n&#xff0c;定义int变量farthest初始化为0&#xff1b; 2.从0~n-1循环每次更新farthes的长度farthest …

2024年3月文章一览

2024年3月编程人总共更新了12篇文章&#xff1a; 1.2024年2月文章一览 2.Programming Abstractions in C阅读笔记&#xff1a;p308-p311 3.Programming Abstractions in C阅读笔记&#xff1a;p312-p326 4.Programming Abstractions in C阅读笔记&#xff1a;p327-p330 5.…

查询卖家已卖出的交易数据

要获取淘宝订单详情数据&#xff0c;你需要使用淘宝开放平台的API来获取数据。以下是获取淘宝订单详情数据的步骤&#xff1a; 在淘宝开放平台上创建一个应用&#xff0c;获取到AppKey和AppSecret。 使用OAuth 2.0授权方式&#xff0c;获取到授权码。 第三方公司授权 使用授…

快速排序(单边循环和双边循环)

快速排序 单边循环快排 pv指向分区中最后一个元素&#xff0c;i&#xff0c;j指向分区中第一个元素&#xff0c;j所指向的元素和pv指向的元素比较大小&#xff0c;如果比pv所指大&#xff0c;则j&#xff0c;否则与i所指元素交换位置&#xff0c;i&#xff0c;j&#xff1b;当…

雪亮工程视频联网综合管理/视频智能分析系统建设方案(二)

一、我国雪亮工程当前建设需求 1&#xff09;加强社会治安防控感知网络建设 加强社会治安防控智能感知网络建设&#xff0c;针对城中村、背街小巷、城乡结合部等重点区域建设安装视频监控设备&#xff0c;减少死角和盲区&#xff0c;与已有感知系统结合&#xff0c;形成高低搭…

树形查找试题(二叉树、红黑树)

一、单项选择题 01.对于二叉排序树&#xff0c;下面的说法中&#xff0c;()是正确的。 A.二叉排序树是动态树表&#xff0c;查找失败时插入新结点&#xff0c;会引起树的重新分裂和组合 B.对二叉排序树进行层序遍历可得到有序序列 C.用逐点插入法构造二叉排序树&#xff0c;若先…

Harmony鸿蒙南向驱动开发-MIPI DSI接口使用

功能简介 DSI&#xff08;Display Serial Interface&#xff09;是由移动行业处理器接口联盟&#xff08;Mobile Industry Processor Interface (MIPI) Alliance&#xff09;制定的规范&#xff0c;旨在降低移动设备中显示控制器的成本。它以串行的方式发送像素数据或指令给外…

Centos安装MySQL提示公钥尚未安装

一、问题 在Centos7.9使用yum安装MySQL时出现错误&#xff0c;提示&#xff1a;mysql-community-server-5.7.44-1.el7.x86_64.rpm 的公钥尚未安装&#xff0c;如下图所示&#xff1a; 执行命令&#xff1a;systemctl start mysqld也提示错误&#xff1a;Failed to start mysq…

目标:3100P 北京最大规模公共智算中心升级 开启建设加速度

近日&#xff0c;企商在线石景山智能算力中心升级备案获批&#xff0c;算力规模由原先的610P跃升至3100P&#xff0c;成为当前北京市规模最大的公共智算中心之一。 石景山智能算力中心效果图 在计算机领域&#xff0c;“P”代表“Petaflop”&#xff0c;1P即每秒1000万亿次浮点…

【JAVA基础篇教学】第三篇:Java循环控制语句

博主打算从0-1讲解下java基础教学&#xff0c;今天教学第三篇&#xff1a;Java循环控制语句。 在Java中&#xff0c;循环控制语句用于重复执行一段代码&#xff0c;直到满足特定条件为止。Java提供了多种类型的循环语句&#xff0c;包括for循环、while循环和do-while循环。 一…

ChatGPT国内镜像站大全(全都是能白嫖的)

今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用ChatGPT吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像站到处都是&a…

Qt中播放GIF动画

在Qt应用程序中&#xff0c;如果你想在QLabel控件上播放GIF动画&#xff0c;可以使用QMovie类与QLabel配合来实现。以下是详细步骤和代码示例&#xff1a; 步骤1&#xff1a;引入必要的头文件 首先&#xff0c;在你的源代码文件中包含QMovie和QLabel相关的头文件&#xff1a;…

Doris 内网安装部署,基于 CentOS 7

实测 CentOS 7.6 和 7.9都可用&#xff0c;CentOS安装包为&#xff1a;标准安装盘DVD版&#xff0c;如果系统安装的是精简版&#xff0c;需要挂载DVD版或者自行下载依赖。 参考文档 快速开始 - Apache Doris Doris 下载地址&#xff1a;2.1.1 ( Latest ) -> x64 ( avx2 )…

通信感知一体化范畴和频谱要求

&#x1f482; 个人主页: 同学来啦&#x1f91f; 版权: 本文由【同学来啦】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助&#xff0c;欢迎关注、点赞、收藏和订阅专栏哦 文章目录 &#x1f31f; 一、覆盖范畴&#x1f534; 1、被动感知&#x1f7e…

基于小程序实现的校园失物招领系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

SQL注入利用 学习- 布尔盲注

布尔盲注适用场景&#xff1a; 1、WAF或者过滤函数完全过滤掉union关键字 2、页面中不再回显具体数据&#xff0c;但是在SQL语句执行成功或失败返回不同的内容 代码分析&#xff1a;过滤关键字 union if(preg_match(/union/i, $id)) { echo "fail"; exit; } 代码…

软考122-上午题-【软件工程】-需求分析

一、软件需求 在进行需求获取之前&#xff0c;首先要明确需要获取什么&#xff0c;也就是需求包含哪些内容。 软件需求是指用户对目标软件系统在功能、行为、性能、设计约束等方面的期望。通常&#xff0c;这些需求包括功能需求、性能需求、用户或人的因素、环境需求、界面需…

Pycharm远程连接服务器配置详解

背景&#xff1a; 相信很多人都遇到了这种情况&#xff0c;日常的开发和程序的验证都需要在linux环境下验证&#xff0c;而我们都是使用本地windows来进行开发或者脚本的编写&#xff0c;然后再push到远程仓库&#xff0c;再到linux环境下pull下来代码验证&#xff0c;这样每次…