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

news2024/11/30 12:26:31

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.coloradmin.cn/o/1586261.html

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

相关文章

Python(1):认识Python并且了解一些简单函数

文章目录 一、Python的优势及其使用场景二、Python环境的安装三、Python中的变量及其命名四、Python中的注释五、一些简单常见的函数和认识ASCII表六、Python导入模块的方式 一、Python的优势及其使用场景 优点&#xff1a; 开发效率高&#xff1a;Python具有非常强大的第三方…

Java——数组练习

目录 一.数组转字符串 二.数组拷贝 三.求数组中元素的平均值 四.查找数组中指定元素(顺序查找) 五.查找数组中指定元素(二分查找) 六.数组排序(冒泡排序) 七.数组逆序 一.数组转字符串 代码示例&#xff1a; import java.util.Arrays int[] arr {1,2,3,4,5,6}; String…

220 基于matlab的考虑直齿轮热弹耦合的动力学分析

基于matlab的考虑直齿轮热弹耦合的动力学分析&#xff0c;输入主动轮、从动轮各类参数&#xff0c;考虑润滑油温度、润滑油粘度系数等参数&#xff0c;输出接触压力、接触点速度、摩擦系数、对流传热系数等结果。程序已调通&#xff0c;可直接运行。 220直齿轮热弹耦合 接触压力…

出游旅行,不能错过的华为nova 12 Ultra4个AI小技巧

随着AI功能的快速普及&#xff0c;让我们的日常生活和工作借助这些工具变得越来越高效。今天就分享4个超级实用的华为nova 12 Ultra自带的AI小技巧&#xff1a;小艺智能魔法抠图、AI消除、图库搜索、小艺帮写&#xff0c;看看有哪些还是你不知道的&#xff1f; 1. 小艺智能…

Windows:Redis数据库图形化中文工具软件——RESP(3)

这个是用于连接redis数据库的软件工具&#xff0c;安装在windows上的图形化界面&#xff0c;并且支持中文&#xff0c;是在github上的一个项目 1.获取安装包 发布 lework/RedisDesktopManager-Windows (github.com)https://github.com/lework/RedisDesktopManager-Windows/rel…

紧急 CCF-C ICPR 2024摘要投稿日期延期至4月10日 速投速成就科研梦

会议之眼 快讯 第27届ICPR&#xff08;The International Conference on Pattern Recognition&#xff09;即国际模式识别会议将于 2024年 12月1日-5日在印度加尔各答的比斯瓦孟加拉会议中心举行&#xff01;ICPR是国际模式识别协会的旗舰会议&#xff0c;也是模式识别、计算机…

OpenHarmony南向开发案例:【智能保险柜】

样例简介 智能保险柜实时监测保险柜中振动传感器&#xff0c;当有振动产生时及时向用户发出警报。在连接网络后&#xff0c;配合数字管家应用&#xff0c;用户可以远程接收智能保险柜的报警信息。后续可扩展摄像头等设备&#xff0c;实现对危险及时报警&#xff0c;及时处理&a…

LeetCode-2529题:正整数和负整数的最大计数(原创)

【题目描述】 给你一个按 非递减顺序 排列的数组 nums &#xff0c;返回正整数数目和负整数数目中的最大值。换句话讲&#xff0c;如果 nums 中正整数的数目是 pos &#xff0c;而负整数的数目是 neg &#xff0c;返回 pos 和 neg二者中的最大值。注意&#xff1a;0 既不是正整…

Linux 线程:线程同步、生产者消费者模型

目录 一、死锁 二、条件变量实现线程同步 1、为什么需要线程同步 2、条件变量、同步、竞态条件 3、条件变量函数&#xff1a;初始化 销毁 等待 唤醒 4、实现简单的多线程程序 不唤醒则一直等待 实现线程同步 三、生产者消费者 1、借助超市模型理解 2、优点 四、基于…

Eland上传bge-base-zh-v1.5向量化模型到ElasticSearch中

最近需要做一些向量检索&#xff0c;试试ES 一、准备 系统&#xff1a;MacOS 14.3.1 ElasticSearch&#xff1a;8.13.2 Kibana&#xff1a;8.13.2 本地单机环境&#xff0c;无集群&#xff0c;也不基于Docker BGE是一个常见的文本转向量的模型&#xff0c;在很多大模型RAG应…

python基础语法--输入和输出

一、 输入 input() python使用input输入变量&#xff0c;input输入的变量为字符串形式&#xff0c;可以通过其他方式转换为整型或其他类型。 &#xff08;1&#xff09;单行读入已知个数的字符串或数字 读入字符串 # 单行读入字符串a,并给出一句输入提示 a input("请…

【汇编语言实战】求三个已知数最大值

C语言描述该程序流程&#xff1a; #include <stdio.h> int main() {int a10,b20,c15;//scanf("%d %d",&a,&b);if(a>b){if(a>c){printf("%d",c);}else{printf("%d",a);}}else{if(b>c){printf("%d",b);}else{pr…

惊!磁盘未初始化,数据如何拯救?

在数字化时代&#xff0c;磁盘作为存储数据的重要载体&#xff0c;其稳定性直接关系到数据的安全。然而&#xff0c;有时我们会遭遇一个令人头疼的问题——磁盘没有初始化。这意味着磁盘无法被操作系统正常识别和使用&#xff0c;其中的数据仿佛被锁进了一个无形的牢笼。那么&a…

2024全面启动:《水资源基础调查实施方案》解读

【引言】 去年&#xff0c;随着自然资源部《关于开展水资源基础调查工作的通知》&#xff08;自然资发〔2023〕230号&#xff09;的发布&#xff0c;一项旨在全面掌握我国水资源状况、助力生态文明建设与经济社会发展的重大工程——水资源基础调查正式启动。 近日&#xff0c…

uniapp小程序给指定的页面新增下拉刷新功能

需求:有些页面需要实时更新数据,但是又不能做实时刷新,所以给用户一个手动下拉刷新指定接口的功能 第一步:在pages.json给页面加"enablePullDownRefresh": true配置 第二步:在指定页面写onPullDownRefresh方法,和methods同级 onPullDownRefresh() {//加个定时器1秒…

在隐私计算应用中和数链具备哪些技术特点?

在加速“可信数字化”进程的背景下&#xff0c;我国区块链产业将在打造新型平台经济&#xff0c;开启共享经济新时代的同时&#xff0c;带动数字经济“脱虚向实”服务实体经济。 和数软件在加速数字化进程的同时&#xff0c;进一步服务实体经济&#xff0c;提高实体经济的活力…

windows安装Redis,Mongo,ES并快速基本掌握开发流程

前言 这里只是一些安装后的基础操作&#xff0c;后期会学习更加深入的操作 基础操作 前言RedisRedis启动idea集成Redisjedis技术 Mongodbwindows版Mongodb的安装idea整合Mongodb ES(Elasticsearch)ESwindows下载ES文档操作idea整合ES低级别ES整合高级别ES整合 Redis Redis是…

如何在项目中引入vue-router

vue3对应vue-router4 先安装最新版的vue-router pnpm i vue-router 在src目录下新建router文件夹&#xff0c;在其下创建index.js文件 在index.js文件下写 // 创建一个路由器&#xff0c;并暴露出去 // 第一步&#xff1a;引入createRouter import {createRouter,createWeb…

Terraform进阶技巧

Terraform 是管理 IaC 的强大工具&#xff0c;常用常新。在这一部分我们将探索 Terraform 的进阶技能&#xff0c;包括 Terraform 模块、远程状态存储、Terraform 工作区以及自定义 Provider。 1、Terraform 模块 Terraform 模块是可复用的 Terraform 代码包&#xff0c;其…

华为 2024 届校园招聘-硬件通⽤/单板开发——第十套

华为 2024 届校园招聘-硬件通⽤/单板开发——第十套 部分题目分享&#xff0c;完整版带答案(有答案和解析&#xff0c;答案非官方&#xff0c;未仔细校正&#xff0c;仅供参考&#xff09;&#xff08;共十套&#xff09;获取&#xff08;WX:didadidadidida313&#xff0c;加我…