Java21 分代 ZGC 和虚拟线程上手体验

(给ImportNew加星标,提高Java技能)


一、导语


Oracle 刚刚发布了 Java21,由于这是最新的 LTS 版本,引起了大家的关注。我也第一时间在个人项目中进行了升级体验。


本文会一探究竟,和大家分享。


二、Java21 更新内容介绍


  • 官方 Release 公告:https://jdk.java.net/21/release-notes

  • 开源中国介绍:https://my.oschina.net/waylau/blog/10112170


新特性一览:


  • JEP 431:序列集合

  • JEP 439:分代 ZGC

  • JEP 440:记录模式

  • JEP 441:switch 模式匹配

  • JEP 444:虚拟线程

  • JEP 449:弃用 Windows 32 位 x86 移植

  • JEP 451:准备禁止动态加载代理

  • JEP 452:密钥封装机制 API

  • JEP 430:字符串模板(预览)

  • JEP 442:外部函数和内存 API(第三次预览)

  • JEP 443:未命名模式和变量(预览)

  • JEP 445:未命名类和实例主方法(预览)

  • JEP 446:作用域值(预览)

  • JEP 453:结构化并发(预览)

  • JEP 448:Vector API(孵化器第六阶段)


其中大家比较关注的是分代 ZGC 和虚拟线程。


三、开箱


下载地址:


  • OpenJDK 版本:https://jdk.java.net/21/

  • Oracle 版本:https://www.oracle.com/java/technologies/downloads/


对比 17


“边框由不锈钢升级为钛金属”


目录结构一致:



模块数量比 17 少一个:



整体大小从 289MB 增加到了 320MB。


四、升级体验


下载



更新 pom




尝试运行


运行报错:


java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'


解决办法:


升级 lombok 至 1.18.30。


原因:https://github.com/projectlombok/lombok/issues/3393


兼容性检查


由于我的项目以前用的 JDK17,本次升级兼容性良好,只发现了一处:系统托盘中使用了 PopupMenu,出现了字符集问题:


五、分代 ZGC 体验


ZGC 在之前的 JDK 版本中也有,这次的分代 ZGC 更是被大家看好,官方的介绍如下:


Applications running with Generational ZGC should enjoy:

Lower risks of allocations stalls, Lower required heap memory overhead, and

Lower garbage collection CPU overhead.

Enable Generational ZGC with command line options -XX:+UseZGC -XX:+ZGenerational


性能测试参考:https://inside.java/2023/09/03/roadto21-performance/


JVM 参数:-XX:+UseZGC -XX:+ZGenerational


  • 使用 Java21,未使用 ZGC


MooInfo 内存占用查看:

  • 使用 Java21,使用分代 ZGC


MooInfo 内存占用查看:




以上只是初步体验,关于ZGC的更多内容,如详细的分代回收情况后续进一步探索。


以上内存占用查看使用我之前做的一个工具,MooInfo:
https://github.com/rememberber/MooInfo



六、虚拟线程探索


Virtual threads are lightweight threads that reduce the effort of writing, maintaining, and debugging high-throughput concurrent applications.


虚拟线程是轻量级线程,可以减少编写、维护和调试高吞吐量并发应用程序的工作量。


Oracle 介绍:https://docs.oracle.com/en/java/javase/20/core/virtual-threads.html#GUID-DC4306FC-D6C1-4BCC-AECE-48C32C1A8DAA


平台线程


Oracle 官方文档的机器翻译:


  • 平台线程是作为操作系统(OS)线程的瘦包装器实现的。

  • 平台线程在其底层操作系统线程上运行Java代码,平台线程在平台线程的整个生命周期内捕获其操作系统线程。

  • 因此,可用平台线程的数量受限于操作系统线程的数量。

  • 平台线程通常有一个大的线程堆栈和其他由操作系统维护的资源。

  • 平台线程支持线程局部变量。

  • 平台线程适合运行所有类型的任务,但可能是有限的资源。


虚拟线程


Oracle 官方文档的机器翻译:


  • 与平台线程一样,虚拟线程也是 java.lang.Thread 的一个实例。

  • 但是,虚拟线程并不依赖于特定的操作系统线程。

  • 虚拟线程仍然在操作系统线程上运行代码。

  • 但是,当虚拟线程中运行的代码调用阻塞 I/O 操作时,Java 运行时会挂起虚拟线程,直到可以恢复为止。

  • 与挂起的虚拟线程关联的操作系统线程现在可以自由地为其他虚拟线程执行操作。


实现原理


虚拟线程的实现方式与虚拟内存类似。为了模拟大量内存,操作系统将较大的虚拟地址空间映射到有限的 RAM。同样,为了模拟大量线程,Java运行时将大量虚拟线程映射到少量操作系统线程。


与平台线程不同,虚拟线程通常具有浅调用堆栈,只执行单个 HTTP 客户端调用或单个 JDBC 查询。尽管虚拟线程支持线程局部变量,但您应该仔细考虑使用它们,因为单个 JVM 可能支持数百万个虚拟线程。


虚拟线程适合运行大部分时间处于阻塞状态、通常等待 I/O 操作完成的任务。但是,它们不适用于长时间运行的 CPU 密集型操作。


虚拟线程用法


Thread thread = Thread.ofVirtual().start(() -> System.out.println("Hello"));thread.join();


或者


try {    Thread.Builder builder = Thread.ofVirtual().name("MyThread");    Runnable task = () -> {        System.out.println("Running thread");    };    Thread t = builder.start(task);    System.out.println("Thread t name: " + t.getName());    t.join();} catch (InterruptedException e) {    e.printStackTrace();}

或者


public class CreateNamedThreadsWithBuilders {        public static void main(String[] args) {                try {            Thread.Builder builder =                Thread.ofVirtual().name("worker-", 0);
Runnable task = () -> { System.out.println("Thread ID: " + Thread.currentThread().threadId()); };
// name "worker-0" Thread t1 = builder.start(task); t1.join(); System.out.println(t1.getName() + " terminated");
// name "worker-1" Thread t2 = builder.start(task); t2.join(); System.out.println(t2.getName() + " terminated"); } catch (InterruptedException e) { e.printStackTrace(); } }}


或者




try (ExecutorService myExecutor = Executors.newVirtualThreadPerTaskExecutor()) { Future<?> future = myExecutor.submit(() -> System.out.println("Running thread")); future.get(); System.out.println("Task completed");} catch (InterruptedException | ExecutionException e) { e.printStackTrace();}

以上是 Java20 文档的用法,实际使用时我发现还可以这样:


 Thread.startVirtualThread(() -> {    // do something   });

平台线程和虚拟线程对比测试


为了测试对比,我建了一个项目:

初步对比,和官网描述一致,计算密集型场景差别不大,IO密集型场景有明显改善
虚拟线程 100 个,IO 读文件


平台线程 100 个,IO 读文件。

虚拟线程 100 个,Get 请求百度首页。

平台线程 100个,Get 请求百度首页。

但是由于是本地测试,且用例比较简陋,无法完全得出准确结论。日后大家有实际 IO 密集性多线程场景可以实际感受下。


线程池?忘了它吧


开发人员通常会将应用程序代码从基于线程池的传统 ExecutorService 迁移到虚拟线程每任务 ExecutorService。


线程池和所有资源池一样,旨在共享昂贵的资源,但虚拟线程并不昂贵,而且永远不需要将它们池化。


七、一颗语法糖?Java21 新特性:Record Patterns


一个例子感受一下新特性:Record Patterns


before


static void printSum(Object obj) {    if (obj instanceof Point p) {        int x = p.x();        int y = p.y();        System.out.println(x+y);    }}


after


static void printSum(Object obj) {    if (obj instanceof Point(int x, int y)) {        System.out.println(x+y);    }}

参考:https://my.oschina.net/didispace/blog/10112428


转自:周波/京东云开发者社区,

链接:blog.csdn.net/jdcdev_/article/details/133698544




- EOF -

推荐阅读  点击标题可跳转

1、Java 21 虚拟线程详解

2、12 张图带你彻底理解 ZGC

3、JDK 21正式发布,史诗级增强!!!


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

点赞和在看就是最大的支持❤️


相关推荐

  • 算法的末日来了!阿里裁掉大量算法,数量远超其他岗位,因为算法拿着高薪,就知道调参发论文!
  • [211渣硕] 腾讯/阿里/携程 详细NLP算法实习 面经
  • Tree of Thoughts(ToT):让大模型能动地解决问题
  • 终于发表了第一篇 CCF A 类顶会!
  • 实现订单 30 分钟未支付则自动取消,我有五种方案!
  • Object o = new Object() 占用多少字节?
  • 美团一面:为什么 MySQL 不建议使用 NULL 作为列默认值?5 分钟彻底搞懂!
  • 语雀停服八小时,P0级事故!故障原因和补偿来了!!
  • 协同发展,生态聚合丨1024程序员节暨「源聚一堂」开源技术沙龙(北京站)成功举办
  • 周鸿祎称程序员节是唯一不能放假的节日;苹果官宣十月 Mac 发布会:来势凶猛;Firefox 119 发布|极客头条
  • 用了这些IDEA插件以后,我写代码快了10倍!
  • 浅谈多人游戏原理和简单实现
  • 【深度学习】激光雷达分割与测距SOTA算法!已开源!
  • 【深度学习】NIPS 2022 表格数据还需要深度学习吗?
  • 【学术相关】教育部:研究生,可以换导师!
  • 文末福利|即将开始!3分钟带你揭晓稀土掘金创新论坛四大亮点,一起探讨AI时代下的管理变革
  • Nodejs 已发布 21.1.0 版本
  • 一文搞懂“支付·清结算·账务”全局
  • 导入个Excel页面直接卡死,看我如何处理T0生产事故~
  • 一篇文章让你搞懂到底什么是 CDN