【JDK21】详解虚拟线程

目录

1.概述

2.虚拟线程是为了解决哪些问题

2.1.线程切换的巨大代价

2.2.哪些情况会造成线程的切换

2.3.线程资源是有限的

3.虚拟线程

4.适用场景


1.概述

你发任你发,我用JAVA8?JDK21可能要对这句话say no了。

现在Oracle JDK是每4个版本,推出一个长期支持版本,JDK21就是前段时间发布的最新的长期支持版JDK。作为最新的长期支持版JDK,JDK21中集合了非常多的重要新特性,其中最为重要,最有意义,最吸引人的莫过于——虚拟线程。虚拟线程虽然不是JDK21才引入的,但是是在该版本中才得以稳定的,所以我们建议要用虚拟线程的话,最好还是使用JDK21。

本文将用一个清晰的思路抽丝剥茧,层层递进的去探讨:

1.虚拟线程是为了解决哪些问题

在虚拟线程出现之前,传统线程模型中线程切换存在的问题,即线程切换的代价。

然后去思考哪些情况下线程会切换?有哪些可以优化的地方?

2.虚拟线程是怎么解决这些问题的

然后是虚拟线程是怎样工作的?是怎样进行任务调度的?

3.虚拟线程的实际应用价值

虚拟线程的适用场景。

2.虚拟线程是为了解决哪些问题

2.1.线程切换的巨大代价

虚拟线程是为了解决哪些问题?自然是为了解决之前传统线程模型存在的一些问题。如果对计算机的线程模型不是很熟悉的同学博主之前有一篇文章,有兴趣可以看一下:

【进程与线程】最好懂的讲解-CSDN博客

所以传统的线程模型存在哪些问题喃?这就要从线程切换的巨大代价聊起。

先给出结论——线程的切换可能会引起CPU上下文的切换,从而造成巨大的CPU资源的浪费。接下来我们聊聊具体原因。

CPU的读写速率是要远远高于内存的读写速率的,为了配平CPU和内存之间速率的差距,CPU和内存之间存在着一个由寄存器组成的中间层,寄存器种会存放着CPU接下来要执行的指令,以及后续可能要执行到的指令以及可能要用到的数据。只有预先装载进去这部分可能要用到的东西才能抹平CPU和内存之间的速率差距,不然每次都要去内存取内容,可能是会拉低CPU的效率的。

但该预先装载哪些内容进寄存器种喃?这里遵循了程序的局部性原理。

程序的局部性原理:

程序在执行的时候呈现出局部性规律,在一段时间内,整个程序的执行仅限于程序中的某一个部分,相应的,执行所访问的存储空间也局限于某个内存区域。局部性又分为时间局部性和空间局部性。时间局部性指的是,如果程序中的某条指令一旦执行,则不久后可能会被再次执行,执行指令时访问的数据单元在不久后会被再次访问。空间局部性指的是,一旦访问了某个存储单元,不久后,其附近的存储单元也将被访问。

CPU上下文切换:

寄存器中存储着当前执行的指令、数据、以及下一条指令在内存中的地址等等事关程序正常运行的关键信息。所以寄存器中存储的内容合称为CPU的上下文。

线程切换就可能会存在这样的问题:CPU上下文中存放的是当前线程的要的东西,可能不是下个线程要用的东西。下个线程被执行的时候,可能CPU的上下文就要换一套。CPU上下文的内容本来就是来自于内存,也就是说切换上下文就是去内存中读取要的东西。CPU的读写速度是远远高于内存的读写速度的,在这个切换过程中CPU没啥事儿可干了,就会造成CPU的浪费。

2.2.哪些情况会造成线程的切换

前面我们已经说了线程切换的巨大代价,接下来我们就要想想,什么情况下线程会切换喃?常见的无非两种情况:

  1. 分给当前线程的时间片到了
  2. 线程阻塞了

我们分情况来讨论,首先是分给当前线程的时间片到了造成的线程切换。这类线程切换有问题吗?能被优化吗?很显然没问题,也不能去动它,因为进程和线程的出现本质上就是为了让多个程序能被交替执行,提升CPU的利用率,所以当前线程时间片到了,它就应该把CPU交出来,其它线程才有机会被执行。

至于第二种,线程阻塞引起的线程切换,就是值得我们进行优化的了。线程阻塞了,只是当前任务阻塞了,线程只是任务的载体,完全可以换个任务在上面继续执行。同理还有一种情况,就是时间片还没有用完,线程上面的任务跑完了,其实也该这样处理。

2.3.线程资源是有限的

除了上面线程切换的问题外,还有一点是值得注意的,就是线程资源是有限的,一台计算机上能开出来的线程是有上限的。

操作系统能够同时开启的线程数量是有限的主要有以下几个原因:

  1. 有限的系统资源:每个线程需要占用一定的系统资源,包括内存、CPU时间片、文件描述符等。系统的资源是有限的,因此开启过多的线程会消耗大量资源,可能导致资源耗尽或者性能下降。

  2. 调度开销:操作系统需要花费时间来管理和调度线程,包括线程的创建、销毁、切换等操作。如果线程数量过多,操作系统的调度开销会增加,降低系统的效率。

总结起来说人话就是,用来描述进程和线程的描述符(PCB或者TCP等)要存在内存中、内存是有限的,所以理论上线程自然是有上限的。

3.虚拟线程

前面罗嗦了这么多,总结起来无非就是计算机的线程资源是有限的,线程的切换会造成CPU资源的浪费。所以如果能复用线程资源,会提升多任务执行的效率。虚拟线程其实就是对线程资源的一种复用。

传统的JDK线程是每一条对应一条操作系统的线程,而虚拟线程是多个虚拟线程对应一条JDK的线程:

虚拟线程体系其实就是依赖于fork join pool来实现的,线程池中的每个JDK线程会对应一个任务列表,列表中会有多个虚拟线程,其实就是多个任务,由fork join pool的调度器来调度虚拟线程(任务)到各条平台线程上去。当有虚拟线程(任务)阻塞或者在时间片内提前执行完了等情况发生,调度器会去调度任务队列中的虚拟线程(任务)到这条平台线程上去继续执行:

ok,终于到虚拟线程的使用了,其实虚拟线程的使用本身反而还没有啥很多要说的。API的设计使用,当然是具有越极致的开闭性越好,所以作为线程的一个补充,虚拟线程的使用,在API层面是很简单的,和传统JDK线程的API语法几乎是一样的。

前置条件就不多说了,肯定是下载安装配置好JDK21,都在看JDK新特性的同学,这个肯定都是小问题,不赘述了。

手动开启虚拟线程:

Thread thread=Thread.ofVirtual().name("vittualThread").unstarted(new Task());
thread.start();

自动开启虚拟线程:

Thread thread=Thread.ofVirtual().name("virtualThread").start(new Task());

线程池:

ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
executorService.submit(new Task( ));
executorService.submit(new Task( ));
executorService.submit(new Task( ));

4.适用场景

虚拟线程采用协作式调度,线程主动放弃 CPU 控制权,而不是由操作系统强制切换。这种调度模型对于 IO 密集型任务非常适用,因为在等待 IO 操作完成的时候,线程可以主动让出 CPU 控制权,允许其他任务继续执行,提高了系统的吞吐量。

然而,在 CPU 密集型计算任务中,线程需要持续占用 CPU 资源进行计算,不适合频繁地放弃 CPU 控制权。虚拟线程的协作式调度可能导致任务在进行计算时频繁地让出 CPU,从而降低了计算密集型任务的执行效率。

虚拟线程对于IO密集型任务的友好,从tomcat11主动去拥抱JDK11,可见一斑:

tomcat作为一个web server,面对网络IO时,使出一手虚拟线程,毫无疑问可以大大拉高系统的吞吐量。

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

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

相关文章

minio分布式存储系统

目录 拉取docker镜像 minio所需要的依赖 文件存放的位置 手动上传文件到minio中 工具类上传 yml配置 config类 service类 启动类 测试类 图片 视频 删除minio服务器的文件 下载minio服务器的文件 拉取docker镜像 拉取稳定版本:docker pull minio/minio:RELEASE.20…

FLASK博客系列6——数据库之谜

我们上一篇已经实现了简易博客界面,你还记得我们的博客数据是自己手动写的吗?但实际应用中,我们是不可能这样做的。大部分程序都需要保存数据,所以不可避免要使用数据库。我们这里为了简单方便快捷,使用了超级经典的SQ…

MySOL常见四种连接查询

1、内联接 &#xff08;典型的联接运算&#xff0c;使用像 或 <> 之类的比较运算符&#xff09;。包括相等联接和自然联接。 内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。例如&#xff0c;检索 students和courses表中学生标识号相同的所有行。 2、…

linux下的工具---vim

一、了解vim 1、vim是linux的开发工具 2、vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅可以在终端运行…

前端OFD文件预览(vue案例cafe-ofd)

0、提示 下面只有vue的使用示例demo &#xff0c;官文档参考 cafe-ofd - npm 其他平台可以参考 ofd - npm 官方线上demo: ofd 1、安装包 npm install cafe-ofd --save 2、引入 import cafeOfd from cafe-ofd import cafe-ofd/package/index.css Vue.use(cafeOfd) 3、使…

计算机服务器中了mallox勒索病毒如何处理,mallox勒索病毒解密文件恢复

科技技术的发展推动了企业的生产运营&#xff0c;网络技术的不断应用&#xff0c;极大地方便了企业日常生产生活&#xff0c;但网络毕竟是一把双刃剑&#xff0c;网络安全威胁一直存在&#xff0c;近期&#xff0c;云天数据恢复中心接到很多企业的求助&#xff0c;企业的计算机…

文件夹重命名技巧:如何整理过长且混乱的文件夹名称

当浏览计算机文件夹时&#xff0c;有时候会遇到一些过长且混乱的文件夹名称&#xff0c;给文件夹管理带来不便。倘若手动修改文件夹名称会出现错误的机率过大&#xff0c;且这样操作太耗费时间和精力。有什么方法能够避免手动修改文件夹名称&#xff0c;提升工作效率的方法呢&a…

万字详解,和你用RAG+LangChain实现chatpdf

像chatgpt这样的大语言模型(LLM)可以回答很多类型的问题,但是,如果只依赖LLM,它只知道训练过的内容,不知道你的私有数据:如公司内部没有联网的企业文档,或者在LLM训练完成后新产生的数据。(即使是最新的GPT-4 Turbo,训练的数据集也只更新到2023年4月)所以,如果我们…

leetCode 841. 钥匙和房间 图遍历 深度优先遍历+广度优先遍历 + 图解

841. 钥匙和房间 - 力扣&#xff08;LeetCode&#xff09; 有 n 个房间&#xff0c;房间按从 0 到 n - 1 编号。最初&#xff0c;除 0 号房间外的其余所有房间都被锁住。你的目标是进入所有的房间。然而&#xff0c;你不能在没有获得钥匙的时候进入锁住的房间。当你进入一个房…

Android 12 打开网络ADB并禁用USB连接ADB

平台 RK3588 Android 12 Android 调试桥 (adb) Android 调试桥 (adb) 是一种功能多样的命令行工具&#xff0c;可让您与设备进行通信。adb 命令可用于执行各种设备操作&#xff0c;例如安装和调试应用。adb 提供对 Unix shell&#xff08;可用来在设备上运行各种命令&am…

保护IP地址不被窃取的几种方法

随着互联网的普及和信息技术的不断发展&#xff0c;网络安全问题日益凸显。其中&#xff0c;保护个人IP地址不被窃取成为了一个重要的问题。IP地址是我们在互联网上的身份标识&#xff0c;如果被他人获取&#xff0c;就可能导致个人隐私泄露、计算机受到攻击等一系列问题。因此…

笔记62:注意力汇聚 --- Nadaraya_Watson 核回归

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章&#xff1a;动手学深度学习~注意力机制 a a a a a a a a a a a a a a a a

常见面试题-Netty中ByteBuf类

了解 Netty 中的 ByteBuf 类吗&#xff1f; 答&#xff1a; 在 Java NIO 编程中&#xff0c;Java 提供了 ByteBuffer 作为字节缓冲区类型&#xff08;缓冲区可以理解为一段内存区域&#xff09;&#xff0c;来表示一个连续的字节序列。 Netty 中并没有使用 Java 的 ByteBuff…

SpringBoot详解

一、介绍 Spring Boot 是一个基于 Spring 框架的开源框架&#xff0c;用于构建微服务和 Web 应用程序。它可以帮助开发者轻松创建独立的、基于 Spring 的应用程序&#xff0c;并在较短的时间内完成项目的开发。 二、核心 1. 约定大于配置 Spring Boot 通过自动化配置、约定优…

【C++】静态成员

静态成员就是在成员变量和成员函数前加上关键字static&#xff0c;称为静态成员。 静态成员分为&#xff1a; 静态成员变量 所有对象共享同一份数据在编译阶段分配内存类内声明&#xff0c;类外初始化 静态成员函数 所有对象共享同一个函数静态成员函数只能访问静态成员变量 …

Java制作“简易王者荣耀”小游戏

第一步是创建项目 项目名自拟 第二部创建个包名 来规范class 然后是创建类 GameFrame 运行类 package com.sxt;import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; im…

Flask 运用Xterm实现交互终端

Xterm是一个基于X Window System的终端仿真器&#xff08;Terminal Emulator&#xff09;。Xterm最初由MIT开发&#xff0c;它允许用户在X Window环境下运行文本终端程序。Xterm提供了一个图形界面终端&#xff0c;使用户能够在图形桌面环境中运行命令行程序。而xterm.js是一个…

使用STM32和蓝牙模块进行无线数据传输的实践

无线数据传输在现代通信领域中具有重要的地位&#xff0c;而蓝牙技术是一种常用的无线数据传输技术。本文介绍了如何使用STM32微控制器和蓝牙模块实现无线数据传输的方案&#xff0c;包括硬件设计、蓝牙模块配置、数据发送和接收等步骤&#xff0c;并给出相应的代码示例。 一、…

学习知识回顾随笔

文章目录 如何远程连接MySQL数据库1.创建用户来运行&#xff0c;此用户从任何主机连接到mysql数据库2.使用IP地址来访问MySQL数据库 如何远程访问Django项目Web应用什么是Web应用应用程序的两种模式Web应用程序的优缺点 HTTP协议&#xff08;超文本传输协议&#xff09;简介HTT…

使用C#和HtmlAgilityPack打造强大的Snapchat视频爬虫

概述 Snapchat作为一款备受欢迎的社交媒体应用&#xff0c;允许用户分享照片和视频。然而&#xff0c;由于其特有的内容自动消失特性&#xff0c;爬虫开发面临一些挑战。本文将详细介绍如何巧妙运用C#和HtmlAgilityPack库&#xff0c;构建一个高效的Snapchat视频爬虫。该爬虫能…