Java21-虚拟线程小试牛刀-meethigher

其他语言,如Go早期就支持了叫做协程的东西,它是轻量化后的线程,而Java异步编程却只有线程的概念。JDK8以后的升级带来的改变总体感觉不大,不过这次JDK21带来的Virtual Thread还是值得体验一把的,可以说是YYDS,终于有理由不使用Java8了!

首先下载JDK 21。

如官方所说,Virtual Thread在JDK19和JDK20时,还是预览版本。在JDK21才正式确定出道。因此现有版本,已经可以正式使用了。

下面所有的Virtual Thread我都叫他虚拟线程了,而不是协程,反正只是个名。

不过新版本发布之后,想要正式使用,还需要等待IDE更新,不然使用体验没那么好。

以下测试都是通过JDK原生编译命令执行。

一、快速入门

1.1 如何创建

Java的虚拟线程,是基于ForkJoinPool线程池实现的,它适用于密集型阻塞场景。

常规情况下,如果存在阻塞,那么线程就会卡在那里了,这段时间是啥也不干,但却又占着茅坑。其实这部分时间还是可以让他做别的事情的,就像netty的事件驱动非阻塞一样,于是虚拟线程应运而生。

说人话就是,虚拟线程适合处理大量阻塞的任务。如果处理计算任务,或者个数较少的阻塞任务,优势并不明显。

Java中的new Thread()获取到的即对应操作系统中的线程。不过在JDK21中,给了他更明确的概念,平台线程PlatformThread。

不求甚解,只求会用。至于如何创建PlatformThread和VirtualThread,请看以下代码。

//线程,即平台线程。两种方式
Thread platformThread = new Thread(new TestRunner(null));
Thread platformThread1 = Thread.ofPlatform().unstarted(new TestRunner(null));
//虚拟线程。跟一下源码,可知他是依赖于池化的ForkJoinPool的
Thread virtualThread = Thread.ofVirtual().unstarted(new TestRunner(null));

1.2 性能比较

下面比较PlatformThread和VirtualThread处理密集型阻塞任务时的执行性能。

import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;public class Main {/*** 基于15个线程池实现的虚拟线程* 执行一万个任务,每个任务耗时1000毫秒,总共耗费2637毫秒*/public static void virtualThread(int count) throws Exception {StopWatcher stopWatcher = new StopWatcher();stopWatcher.start();CountDownLatch countDownLatch = new CountDownLatch(count);for (int i = 0; i < count; i++) {Thread.ofVirtual().start(new TestRunner(countDownLatch));}countDownLatch.await();stopWatcher.stop();System.out.printf("本次执行耗时:%s毫秒", stopWatcher.getTimeInterval().toMillis());}/*** 基于15个池化线程* 执行一万个任务,每个任务耗时1000毫秒,总共耗费11分钟*/public static void platformThread(int count) throws Exception {StopWatcher stopWatcher = new StopWatcher();stopWatcher.start();CountDownLatch countDownLatch = new CountDownLatch(count);for (int i = 0; i < count; i++) {CompletableFuture.runAsync(new TestRunner(countDownLatch));}countDownLatch.await();stopWatcher.stop();System.out.printf("本次执行耗时:%s毫秒", stopWatcher.getTimeInterval().toMillis());}public static void main(String[] args) throws Exception {int count = 10000;//virtualThread(count);platformThread(count);}public static class TestRunner implements Runnable {private final CountDownLatch countDownLatch;public TestRunner(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@Overridepublic void run() {try {System.out.println(Thread.currentThread() + " start " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));Thread.sleep(1000);System.out.println(Thread.currentThread() + " stop " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));countDownLatch.countDown();} catch (Exception e) {System.out.println(e.getMessage());}}}public static class StopWatcher {private long start;private long stop;public StopWatcher() {}public void start() {this.start = System.currentTimeMillis();}public void stop() {this.stop = System.currentTimeMillis();}public Duration getTimeInterval() {return Duration.ofMillis(this.stop - this.start);}}
}

进行编译,并运行。

javac Main.java && java Main

两种方式,分别模拟处理10000个阻塞任务,每个任务阻塞1秒。

  • PlatformThread: 15个池化线程
  • VirtualThread: 15个池化线程,但是采用了虚拟线程方式

我的硬件情况就不详细描述了,直接对比结果,就能清楚明了感受到差异。

执行结果

  1. 耗时对比
    • PlatformThread: 耗时11分钟
    • VirtualThread:耗时2秒
  2. CPU使用率对比
    • PlatformThread: 占用10%左右
    • VirtualThread: 占用50%左右

综上,处理密集型阻塞任务,使用VirtualThread更大程度发挥了CPU性能!

此处官方已经明确说了,虚拟线程只适合密集型阻塞场景。假如像计算型,反而会降低性能。

说白了,虚拟线程就是压榨CPU空闲的时间,不允许他闲下来。这点跟操作系统的时间片、Netty的事件驱动类似。

二、实际案例

2.1 购物

请看如下代码

像findUserByName和loadCardFor是通过数据库查询,其实在查询的过程中,将请求发给数据库,等待数据库响应的过程就是阻塞的。

这种顺序执行的情况,其中就存在CPU利用不充分的问题,就可以使用异步编程提升性能。但是采用多线程能提高性能吗?

先分析下业务,这是一个购物过程。

  1. 用户:查询并获取用户
  2. 购物车:通过用户查询并获取购物车,获取购物车的总价格
  3. 订单:支付该用户的总价格对应的费用,获取订单
  4. 通知:通知用户订单信息

会发现这里面是环环相扣的,没有并行的业务。

即使我们把代码进行了异步如下,有意义吗?没意义!

假如同时来100个请求,会发现,阻塞时间的总量根本没变。性能并没有提升。

2.1 购物-优化版

那么如何提升性能?就得通过阻塞入手了,让他变成不阻塞。这样单位时间内处理的请求就更多了。

而且,也不能采用上述异步后的代码形式,因为他难以阅读、难以调试。

我们希望他

  1. 不阻塞
  2. 易阅读、易调试

那么如何优化呢?请看如下代码。

private ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();public void pay(String name) throws Exception {executor.submit(() -> {User user = userService.findUserByName(name);if (!repo.contains(user)) {repo.save(user);}var cart = cartService.loadCartFor(user);var total = cart.items().stream().mapToInt(Item::price).sum();var transactionId = paymentService.pay(user, total);emailService.send(user, cart, transactionId);})
}

使用虚拟线程,既能易调试、易阅读,而且将原来阻塞的时间用来处理更多的请求。**这些内部执行过程,都由Java自行处理,不需要开发者关心。**用老外的话说,”这不是魔术,这只是工程化“。

如果不理解,建议将1.2的代码亲自调试一下。

三、参考致谢

JEP 444: Virtual Threads

Java 21 new feature: Virtual Threads #RoadTo21 - YouTube

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

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

相关文章

【多线程】Lambda表达式

package org.example;public class TestLambda {public static void main(String[] args) {Like likenew Like();like.lambda();}}//定义一个函数式接口 interface ILike{void lambda(); }//实现类 class Like implements ILike{Overridepublic void lambda() {System.out.prin…

网络层重要协议 --- IP协议

小王学习录 今日摘录IP数据报数据报首部IPv4的局限及解决方法 地址管理路由选择扩展&#xff1a;NAT和NAPT的结合使用 今日摘录 关山难越&#xff0c;谁悲失路之人。萍水相逢&#xff0c;尽是他乡之客。 网络层的职责是地址管理和路由选择&#xff0c;在网络层中最重要的协议…

SpringBoot整合Mybatis-plus

MyBatis-Plus与MyBatis区别&#xff1a; 导入坐标不同数据层实现简化 1.创建项目 2.选择依赖 3.pom文件 说明&#xff1a;配置pom.xml文件 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId>&…

【Java 进阶篇】JSP 简单入门

在现代Web开发中&#xff0c;JavaServer Pages&#xff08;JSP&#xff09;是一项非常重要的技术。JSP允许开发者将Java代码嵌入HTML页面&#xff0c;以实现动态内容的生成和呈现。本文将详细介绍JSP的概念、原理以及如何使用JSP来构建Web应用程序。 第一部分&#xff1a;JSP …

jetsonTX2 nx配置yolov5和D435I相机,完整步骤

转载一篇问题解决博客&#xff1a;问题解决 一、烧录系统 使用SDK烧录 二、安装archiconda3 JETSON TX2 NX的架构是aarch64,与win10,linxu不同,所以不能安装Anaconda&#xff0c;这里安装对应的archiconda。 1. 安装 wget https://github.com/Archiconda/build-tools/rel…

Excel·VBA工作表导出为图片

《Excel转图片别再截图啦&#xff01;用这4个方法&#xff0c;高清且无损&#xff01;》&#xff0c;excel转为图片一般方法较为简单&#xff0c;那么能否使用vba将excel转为图片 选中区域导出为图片 zoom设置为2&#xff0c;导出图片较为清晰 Sub 选中区域导出为图片()Dim …

一个使用uniapp+vue3+ts+pinia+uview-plus开发小程序的基础模板

uniappuviewPlusvue3tspiniavite 开发基础模板 使用 uniapp vue3 ts pinia vite 开发基础模板&#xff0c;拿来即可使用&#xff0c;不要删除 yarn.lock 文件&#xff0c;否则会启动报错&#xff0c;这个可能和 pinia 的版本有关&#xff0c;所以不要随意修改。 拉取代码…

HarmonyOS列表组件

List组件的使用 import router from ohos.routerEntry Component struct Index {private arr: number[] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]build() {Row() {Column() {List({ space: 10 }) {ForEach(this.arr, (item: number) > {ListItem() {Text(${item}).width(100%).heig…

JavaEE的渊源

JavaEE的渊源 1. JavaEE的起源2. JavaEE与Spring的诞生3. JavaEE发展历程&#xff08;2003-2007&#xff09;4. JavaEE发展历程&#xff08;2009-至今&#xff09;5. Java的Spec数目与网络结构 1. JavaEE的起源 我们首先来讲一下JavaEE的起源 ,为什么要来讲起源 &#xff1f; …

Spring Boot 整合RabbitMQ

系列文章目录 第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 第七章 Spring Cloud 之 GateWay 第八章 Sprin…

Technology Strategy Pattern 学习笔记2-Creating the Strategy-World Context

Creating the Strategy-World Context 1 PESTEL 1.1 从6个方案看外部 PoliticalEconomicSocialTechnologicalEnvironmentalLegal 1.2 参考URL https://zhuanlan.zhihu.com/p/192522082https://www.docin.com/p-449396129.htmlhttps://blog.csdn.net/xiaoyw71/article/deta…

全球首款双模型AI手机METAVERTU2,为用户开发“第二大脑”

在2023年11月1日&#xff0c;英国奢侈手机品牌VERTU在香港举办了一场新品发布会&#xff0c;它推出了一款全新的AI手机称为METAVERTU2&#xff0c;这是全球首款双模型AI手机。此款手机将Web3技术与人工智能相结合&#xff0c;通过AI模型标记数据和AI Agent的方式&#xff0c;将…

sqlsugar查询数据库下的所有表,批量修改表名字

查询数据库中的所有表 using SqlSugar;namespace 批量修改数据库表名 {internal class Program{static void Main(string[] args){SqlSugarClient sqlSugarClient new SqlSugarClient(new ConnectionConfig(){ConnectionString "Data Source(localdb)\\MSSQLLocalDB;In…

VulnHub jarbas

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

Zabbix监控联想服务器的配置方法

简介 图片 随着科技的发展&#xff0c;对于数据的敏感和安全大部分取决于对硬件性能、故障预判的监测&#xff0c;由此可见实时监测保障硬件的安全很重要&#xff0c;从而衍生了很多对硬件的监测软件&#xff0c;Zabbix就一个不错的选择。开源 开源 开源&#xff01; zabbix是…

[极客大挑战 2019]Knife 1(两种解法)

题目环境&#xff1a; 这道题主要考察中国菜刀和中国蚁剑的使用方法 以及对PHP一句话木马的理解 咱们先了解一下PHP一句话木马&#xff0c;好吗&#xff1f; **eval($_POST["Syc"]);** **eval是PHP代码执行函数&#xff0c;**把字符串按照 PHP 代码来执行。 $_POST P…

基于单片机的超声波测距仪

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、本课题研究的主要内容二、超声波测距仪的整体方案2.2 超声波测距仪设计原理 三、超声波测距仪系统硬件电路的设计3.1 超声波测距仪的基本结构 四、 超声波测距仪系统的软件设计4.1 主程序软件设计仿真 五、结…

大数据之陌陌聊天数据分析案例

目录 目标需求 数据内容 基于Hive数仓实现需求开发 1.建库建表、加载数据 2.ETL数据清洗 3需求指标统计 目标需求 基于Hadoop和hive实现聊天数据统计分析&#xff0c;构建聊天数据分析报表 1.统计今日总消息量 2.统计今日每小时消息量&#xff0c;发送和接收用户数 3.…

用户态内存映射

内存映射不仅仅是物理内存和虚拟内存之间的映射&#xff0c;还包括将文件中的内容映射到虚拟内存空间。这个时候&#xff0c;访问内存空间就能够访问到文件里面的数据。而仅有物理内存和虚拟内存的映射&#xff0c;是一种特殊情况。 对于堆的申请来讲&#xff0c;mmap 是映射内…

Linux Framebuffer驱动框架、接口实现和使用

Linux 驱动-Frame Buffer代码分析 Framebufferfbmem.c部分代码分析初始化 Framebuffer 对于驱动开发人员来说&#xff0c;其实只需要针对具体的硬件平台SOC和具体的LCD&#xff08;通过焊接连接到该SOC引脚上的LCD&#xff09;来进行第一部分的寄存器编程&#xff08;红色部分&…