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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024 年“认证杯”数学中国数学建模网络挑战赛

题目 C题 云中的海盐 巴黎气候协定提出的目标是&#xff1a;在 2100 年前&#xff0c;把全球平均气温相对于工业 革命以前的气温升幅控制在不超过 2 摄氏度的水平&#xff0c;并为 1.5 摄氏度而努力。 但事实上&#xff0c;许多之前的研究已经指出&#xff0c;全球的碳排放以及…

主从数据同步原理

2.2.主从数据同步原理 2.2.1.全量同步 主从第一次建立连接时&#xff0c;会执行全量同步&#xff0c;将master节点的所有数据都拷贝给slave节点&#xff0c;流程&#xff1a; 这里有一个问题&#xff0c;master如何得知salve是第一次来连接呢&#xff1f;&#xff1f; 有几个…

嵌入式C语言(十四)

在现代操作系统架构中&#xff0c;内核空间和用户空间之间增加了一个中间层&#xff0c;这就是系统调用层。 系统调用层主要有如下作用。 为用户空间程序提供一层硬件抽象接口。这能够让应用程序编程者从学习硬件设备底层编程中解放出来。例如&#xff0c;当需要读写一个文件…

Linux操作系统的学习

Linux系统的目录结构 / 是所有目录的顶点目录结构像一颗倒挂的树 Linux常用命令 常见命令 序号命令对应英文作用1lslist查看当前目录下的内容2pwdprint work directory查看当前所在目录3cd [目录名]change directory切换目录4touch [文件名]touch如果文件不存在&#xff0c;新…

TRX节点部署

安装说明 安装版本&#xff1a;4.7.4 安装目录&#xff1a;/data/docker-compose/trx 配置文件路径&#xff1a;/data/docker-compose/trx/config.conf 节点data数据&#xff1a;/data/docker-compose/trx/output-directory 安装方式&#xff1a;Dockerfiledockerdocker-compos…

数字化采购管理系统有什么作用?

数字化采购管理系统是一种以计算机技术为基础&#xff0c;用于管理和优化采购过程的信息系统&#xff0c;它的作用主要体现在以下几个方面&#xff1a; 1、优化采购流程&#xff1a;数字化采购管理系统可以帮助企业建立和优化采购流程&#xff0c;通过设定采购流程的各个环节和…

6.11物联网RK3399项目开发实录-驱动开发之定时器的使用(wulianjishu666)

嵌入式实战开发例程【珍贵收藏&#xff0c;开发必备】&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1tkDBNH9R3iAaHOG1Zj9q1Q?pwdt41u 定时器使用 前言 RK3399有 12 个 Timers (timer0-timer11)&#xff0c;有 12 个 Secure Timers(stimer0~stimer11) 和 2 个 …

鸿蒙实战开发-如何实现标准化数据定义与描述的功能。

介绍 本示例主要使用ohos.data.uniformTypeDescriptor 展示了标准化数据定义与描述的功能&#xff0c;在新增预置媒体文件后&#xff0c;对媒体文件的utd标准类型获取、utd类型归属类型查询、获取文件对应的utd类型的默认图标、支持自定义数据类型等功能。 实现过程中还使用到…

编译安装nginx

nginx版本 安装组件 yum -y install openssl-devel pcre-devel zlib-devel ./configure --with-http_ssl_module && make && make installln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx

【C++算法模板】背包九讲(上):01背包、完全背包、多重背包

文章目录 1&#xff09;01背包1&#xff1a;二维数组2&#xff1a;一维数组 2&#xff09;完全背包1&#xff1a;朴素做法2&#xff1a;公式优化3&#xff1a;再优化一维数组 3&#xff09;多重背包1&#xff1a;朴素做法2&#xff1a;二进制优化3&#xff1a;单调队列优化 1&a…

AcWing-直方图中最大的矩形

131. 直方图中最大的矩形 - AcWing题库 所需知识&#xff1a;单调栈 思路&#xff1a;要求最大矩形&#xff0c;所以需要使矩形的高与长的乘积最大即可&#xff0c;依次从左到右将每一列当作中心列&#xff0c;向两边扩散&#xff0c;直到两边的高都小于该列的高&#xff0c;…

Vmware虚拟机Centos7固定IP地址

1、点击编辑-虚拟网络编辑器 2、点击更改设置、修改虚拟网络配置器并确认保存&#xff08;见图&#xff09; 这个子网IP和子网掩码的前三位需要一样网关的前三位需要和子网ip一致。 3、打开设置“网络和Internet”&#xff0c;点击“更改适配器选项”&#xff0c;点击适配器VM…

PP-LCNet:一种轻量级CPU卷积神经网络

PP-LCNet: A Lightweight CPU Convolutional Neural Network 最近看了一个新的分享&#xff0c;在图像分类的任务上表现良好&#xff0c;具有很高的实践意义。 论文&#xff1a; https://arxiv.org/pdf/2109.15099.pdf项目&#xff1a; https://github.com/PaddlePaddle/Padd…

JUC并发编程2(高并发,AQS)

JUC AQS核心 当有线程想获取锁时&#xff0c;其中一个线程使用CAS的将state变为1&#xff0c;将加锁线程设为自己。当其他线程来竞争锁时会&#xff0c;判断state是不是0&#xff0c;不是自己就把自己放入阻塞队列种&#xff08;这个阻塞队列是用双向链表实现&#xff09;&am…

探索ChatGPT-Plus:AI 助手全套开源解决方案

探索ChatGPT-Plus&#xff1a;AI 助手全套开源解决方案 ChatGPT-plus是一种新型的对话生成模型&#xff0c;它是在OpenAI的ChatGPT基础上进行了改进和优化的版本。ChatGPT-plus的出现引起了广泛关注&#xff0c;因为它在对话生成方面展现出了更加出色的表现和能力。在本文中&am…

蒙特卡洛方法【强化学习】

强化学习笔记 主要基于b站西湖大学赵世钰老师的【强化学习的数学原理】课程&#xff0c;个人觉得赵老师的课件深入浅出&#xff0c;很适合入门. 第一章 强化学习基本概念 第二章 贝尔曼方程 第三章 贝尔曼最优方程 第四章 值迭代和策略迭代 第五章 强化学习实践—GridWorld 第…

flutter嵌入原生view

一、iOS端(Swift实现) 1. 新建原生view(NativeView.swift) &#x1f3f7;️ 需要继承FlutterPlatformView &#xff0c;实现view()方法 import Foundation import Flutterclass NativeView: NSObject, FlutterPlatformView {private var _view: UIViewinit(frame: CGRect,view…

从文件夹(包含子文件夹)找到的包含特定关键词的 Word 文档复制到一个新的文件夹中

import os import glob import shutildef find_and_copy_docs(root_dir, keyword, target_dir):"""在指定的目录及其子目录下查找包含特定关键词的 Word 文档&#xff0c;并将它们复制到目标文件夹。参数:root_dir: 要搜索的根目录路径。keyword: 需要匹配的关键…

make/makefile学习

文章目录 1、makefile函数1.1、字符串替换函数&#xff1a;subst1.2、模式字符串替换函数&#xff1a;patsubst1.3、去空格函数&#xff1a;strip1.4、查找字符串函数&#xff1a;findstring 2、、:、&#xff1f;区别 1、makefile函数 1.1、字符串替换函数&#xff1a;subst …

TS中,PropType导入报错is a type and must be imported using a type-only import...

报错信息&#xff1a; PropType is a type and must be imported using a type-only import when verbatimModuleSyntax is enabled.ts(1484) (alias) type PropType<T> PropConstructor<T> | PropConstructor<T>[] import PropType问题分析&#xff1a; …