Java 21 官方速览:全面拥抱虚拟线程

发布时间 2023-12-01 11:05:28作者: 程序员济癫

前言

首先,感谢一下不少xdm私信关心我的身体状况,我也不是什么厉害的大佬,点开通知看到一堆私信还是蛮感动的。

近来有意大幅缩短了更新频率,增加了日常调养身体的时间,淋巴结确实变小了,睡眠也逐渐正常,每晚23:30-00:00之间就可以自然入睡,所以精神也好了许多。

上周感冒比较严重,这周才好转,加上一直以来的胃病还没好,这具身体着实千疮百孔,令人叹息,慢慢来吧,xdm在学习之余也要保重自己哦。

本篇主要内容是最近 Java 21 正式发布以后,我对于官方说明的翻译、整理、排版,以及一点个人注释加展望,帮助大家节省时间,及时了解下Java的最新动态。

本篇不包含任何案例说明,因为我认为几年内 Java 21 不会在国内有广泛应用,所以暂时不需要去学习它,相反,Java 17 的一些新特性可以考虑学习下,我之前的文章里面已经有这样的专题。

之所以要了解这种重大改进的新版本,目的是为了确定Java未来的趋势走向,就比如之前lamaba表达式和stream流的出现,基本能确定是一种趋势,事实证明也确实如此。

正式发布

在今年9月19日Oracle宣布 Java 21 正式发布,JavaSE的产品经理对于这次版本发布做了全面的说明。

首先是对 Java 21 的一个社区贡献做了说明,在 Java 21 中,标记为已修复的2,585个JIRA问题中,Oracle完成了1,868个问题,而Java社区的其他成员贡献了717个问题。

可以看出来,Java社区非官方的贡献度超过官方团队的三分之一,活跃度依旧是挺高的。

接下来甲骨文着重感谢了一些知名IT公司的研发团队对于 Java 21 的贡献和支持,感谢了亚马逊、ARM、Azul、谷歌、华为、IBM、英特尔、ISCAS、红帽、Rivos、SAP和腾讯等公司的开发人员做出的杰出贡献。

以及来自BellsoftLoongson等小型组织以及独立开发人员的贡献,他们总共贡献了Java 21 中 8% 的修复。

这里可以看到国内一些比较有名的公司,比如华为、腾讯,但有意思的是,总感觉少了谁……

此外,通过OpenJDK质量推广计划,还感谢以下自由/开源软件项目,它们在测试Java 21早期访问构建时提供了出色的反馈,帮助提高了发布的质量:

  • Apache Commons
  • Apache ZooKeeper
  • AssertJ
  • BNYM Code Katas
  • JUnit5
  • Karate
  • MyBatis

这些开源软件项目里面,我稍微插一句,Zookeeper出现在里面,说明它对于 Java 21 的大力支持,能被官方点名感谢,可以变相说明它和Java长期的良好合作。

Zookeeper在国外其实是很受欢迎的,但在国内因为生态环境的差异,并没有受到较高的重视,我建议大家可以多学点Zookeeper,这个中间件非常值得学习。

新增功能

除了数以千计的性能、稳定性和安全性更新之外,Java 21 还提供了数十个新特性和增强,其中15个增强非常重要,足以保证它们有自己的JDK增强建议(jep),包括6个预览特性1个孵化特性

从这里可以看出,Java 21 是 Java 17 之后另一个非常重要的版本。

接下来,我会把发布特性和预览孵化特性分开排列,里面有些特性对于大部分研发人员来说其实可以只做了解,但部分重要特性我会着重说明,大家可以重点关注下。

我专门整理了思维导图,需要的自行在Gitee下载即可。

image

发布特性

正式发布的特性,都是未来需要领悟的特性,类似于 Java 8 发布之后那些新特性一样,会逐渐成为Java未来版本的中流砥柱。

1、JEP 440: Record Patterns

通过扩展模式匹配来表达更复杂、可组合的数据查询,提高Java编程语言的生产力。

这个特性是在Java19-20都是预览特性,Java 21 正式发布。

2、JEP 441: switch增强

通过允许在 case 标签中使用模式,扩展了 switch 表达式和语句的表达性和适用性。

这个特性在Java17-20都是预览特性,Java 21 正式发布。

3、JEP 444: 虚拟线程

采用简单的请求一线程模式编写的服务器应用程序能够以接近最佳的硬件利用率进行扩展。

使现有使用java.lang.Thread API的代码能够在最小的修改下采用虚拟线程。

使用现有的JDK工具,能够轻松进行虚拟线程的故障排除、调试和性能分析。

通过引入虚拟线程,加速应用程序开发效率,尤其适用于云环境中的应用程序,虚拟线程是一种轻量级线程,极大地减少了编写、维护和观察高吞吐量并发应用程序的工作量。

这个特性在Java19-20都是预览特性,Java 21 正式发布。

这是 Java 21 版本最重要的一个改进,也是 Project Loom 项目一直推进的很重要的一环。

Project Loom 大家可以了解下,是Java/JVM 生态系统(由 OpenJDK 托管)中的一个较新的项目,它试图解决传统并发模型中的限制,目标是显著减少编写、维护和观察高吞吐量并发应用程序的工作量。

说白了,就是Oracle为了改进Java高并发难题推进的一个新项目。

虚拟线程直白点讲,就是其他编程语言中已经出现过的协程,也是Java一直被诟病的一点,这次终于在正式版本发布出来了,我觉得值得鼓励,是个好势头。

我期望Java把其他编程语言的优点都抄过来,像什么Python、C#、Go,都抄过来,使劲抄,用力抄,我举双手双脚欢迎。

4、JEP 431: 有序集合

引入新的接口,用于表示按照定义好的遇见顺序进行排列的集合。每个这样的集合都有一个明确定义的第一个元素、第二个元素,依此类推,直到最后一个元素。

为访问其第一个和最后一个元素提供统一的API,并为按相反顺序处理其元素提供统一的API。

通过提供一组统一的操作,它们适用于表示具有定义好顺序的元素序列的集合类型,提高开发者的生产力。

在Java中,集合是非常常用的工具,这个有序集合算是对于集合工具的加强,是否实用现在还不好说,毕竟现有的工具也足以解决绝大部分问题。

5、JEP 439: 分代ZGC

通过扩展 Z Garbage Collector (ZGC) 来维护年轻对象和老年对象的不同分代,提高应用程序的性能。

使用 Generational ZGC 运行的应用程序应该享有以下好处:

  • 较低的分配停顿风险。
  • 较低的堆内存开销要求。
  • 较低的垃圾回收 CPU 开销。

相比非分代 ZGC,这些好处应该不会显著降低吞吐量。同时,非分代 ZGC 的关键特性应该得以保留:

  • 暂停时间不应超过 1 毫秒。
  • 支持从几百兆字节到数太字节的堆大小。
  • 最低限度的手动配置需求。

这是另一个非常重要的改进,主要为了减少垃圾回收对应用程序的停顿时间,是一种低延迟的垃圾回收器

ZGC实现了许多创新的技术,例如并发的垃圾回收、分代堆布局、柔性内存操作等,可以说非常适用于需要低延迟和高吞吐量的应用

但并不是有了ZGC,你什么应用都用ZGC,在一些特定情况下它也可能会对应用程序的吞吐量产生一些小幅度的影响,所以要根据具体应用场景的需求进行评估和选择。

这个改进大家现在只需了解,但以后要花时间研究,对于大部分Java程序员而言,你可能不会真正接触到它,但是你避不开JVM相关的面试题啊……这东西可以预见未来会是Java面试题中的主角之一。

6、JEP 452: 密钥封装机制API

引入密钥封装机制(KEM)的API,KEM是一种使用公钥加密的加密技术,用于保护对称密钥的安全性

使应用程序能够使用KEM算法,例如RSA Key Encapsulation Mechanism (RSA-KEM),椭圆曲线综合加密方案 (ECIES),以及国家标准与技术研究所(NIST)后量子密码标准化过程中的候选KEM算法。

使KEM在更高级的协议,如传输层安全 (TLS),以及混合公钥加密 (HPKE, RFC 9180)等密码方案中使用。

允许安全提供者在Java代码或本地代码中实现KEM算法。

包括在RFC 9180的§4.1中定义的Diffie-Hellman KEM (DHKEM)的实现。

7、JEP 449: 弃用并删除32位x86端口

弃用Windows 32位x86(x86-32)端口,并计划在未来的发布版本中删除它

更新构建系统,在尝试为Windows 32位x86(x86-32)配置构建时发出错误信息。可以通过一个新的配置选项来抑制错误信息。

在相关文档中标记该端口和相关的端口特定功能为弃用,以便将来删除。

这个是可以预见的,因为这个平台的使用率已经下降很多了,对于未来Java的发展和创新是一种阻碍,同样也是为了更好的支持虚拟线程,所以弃用完全说得通。

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

当代理以动态方式加载到运行的JVM中时,发出警告。这些警告旨在为将来的发布版本做准备,该版本默认情况下禁止动态加载代理,以提高默认的完整性。在任何发布版本中,启动时加载代理的可操作性工具不会导致发出警告。

为将来的JDK发布版本做准备,默认情况下禁止在正在运行的JVM中加载代理。

重新评估可操作性(Serviceability)与完整性之间的平衡,可操作性涉及对运行中的代码进行临时更改,而完整性假定运行中的代码不会随意更改。

确保大多数工具——不需要动态加载代理的工具——不受影响。

将动态加载代理的能力与其他所谓的“超级”功能(如深层反射)保持一致。

这个需要大家关注一下,我认为主要目的是因为安全性,Java一向对于安全性是有较高要求的,动态代理本身存在潜在的安全风险。

Java 21 这里说的目前只是会发出警告,但是也暗示了给未来的版本做准备,没说是什么准备,搞不好有可能会弃用了。

预览孵化特性

预览和孵化的特性只需要选择性了解就行,因为有些预览版本不一定最终会正式发布,是有待商榷的,最终根据社区的反馈才能决定是否正式启用。

1、JEP 430: 字符串模板

通过简化包含运行时计算值的字符串的表达简化了Java程序的编写,增强Java编程语言的可读性、可写性和可维护性。

这个我觉得最终会正式发布的,参考了其他编程语言的优点,对于一向被诟病写法较重的Java来说,也是喜人的改变,如果一直以这种势头前进,依然值得大家拥抱这门语言。

2、JEP 443: 未命名的模式和变量

通过引入未命名模式,对Java语言进行增强,未命名模式可以匹配记录组件,而无需指定组件的名称或类型;

引入未命名变量,这些变量可以进行初始化但不会被使用。这两者都以下划线字符 _ 表示

通过省略不必要的嵌套模式,提高记录模式的可读性;

通过确定必须声明但将不会被使用的变量(例如在catch子句中),提高所有代码的可维护性。

这个和上一个类似,也是增强Java的实用性,比较典型的应用就是try..catch、for循环、switch等等,都可以用到。

3、JEP 445: 未命名类和实例主方法

通过减少样板代码和仪式感,使学生能够编写他们的第一个程序,而无需理解为大型程序设计的语言特性。

这个纯粹是降低入门门槛了,感觉有一丁点Python那味儿了。

4、JEP 446: 作用域值

在线程内部和跨线程之间实现不可变数据的共享。

  • 易用性 - 提供了一种编程模型,可以在线程内部和子线程之间共享数据,以简化对数据流的推理。
  • 可理解性 - 从代码的语法结构中明确共享数据的生命周期。
  • 健壮性 - 确保只有合法的调用方可以访问调用方共享的数据。
  • 性能 - 将共享数据视为不可变的,以便允许大量线程的共享,并实现运行时优化。

在Java20中经历了一次孵化

这个特性值得关注,不仅能避免数据泄露或窜改,还可以在线程之间共享,相当于减少了复制,提高了性能。

5、JEP 453: 结构化并发

通过引入结构化并发的API,简化并发编程

提倡一种并发编程风格,可以消除由取消和关闭引起的常见风险,如线程泄漏和取消延迟。

改进并发代码的可观察性。

通过引入结构化并发的API,简化并发编程。它简化了错误处理和取消操作,提高了可靠性,并增强了可观察性。

在Java19-20中经历了两次孵化

这个虽然在孵化,但我不得不说,是Java正在极力解决高并发编程难题的明显迹象之一,前面的虚拟线程、作用域和这个特性都是相辅相成,已经充分说明Java正在 Java 8 的版本之后,再次决定对于并发编程做一次进化。

CompleTableFuture虽然已经很不错了,但只是对旧版本任务编排的优化,并没有解决Java本身并发编程的难题,所以在生产环境应用起来依然容易造成人为缺陷。

至少我使用时是慎之又慎的,一定要分析清楚场景和评估可能的风险,才会决定在某个功能上使用。

但有了虚拟线程之后,加上作用域、结构化并发,我认为是一种进化吧,虽然这些已经不是创新了,但期待值很高。

还有一点需要考量,Java本身迭代到目前这个版本,我认为有些地方不是那么容易更改的,这次能优化到什么程度,是不是真的好用,我还是保留怀疑态度。

6、JEP 442: 外部函数和内存 API

引入一个API,使Java程序能够与Java运行时之外的代码和数据进行交互

通过有效地调用外部函数(即JVM之外的代码)和安全地访问外部内存(即JVM不管理的内存),该API使Java程序能够调用本地库和处理本地数据,而不会出现JNI的脆弱性和危险性。

  • 易用性 - 用优秀的纯Java开发模型替代了Java Native Interface (JNI)。
  • 性能 - 提供与JNI和sun.misc.Unsafe等现有API相当甚至更好的性能。
  • 通用性 - 提供操作不同类型的外部内存的方法(例如本地内存、持久内存和托管堆内存),以及随着时间的推移适应其他平台(例如32位x86)和其他语言编写的外部函数(例如C++、Fortran)。
  • 安全性 - 允许程序对外部内存执行不安全的操作,但默认情况下提醒用户此类操作存在风险。

在Java19-20中经历了两次预览

这个就是用来替代JNI的,JNI什么尿性不用多说了吧,臭名昭著了。

7、JEP 448: 向量API

引入一个API,用于表达向量计算,在支持的CPU架构上可靠地在运行时编译为最佳向量指令,从而实现比等效的标量计算更高的性能。

以下是一些显著的改变:

  • 向向量掩码中添加了异或(xor)操作。
  • 改进了向量洗牌的性能,特别是在重新排列向量元素和在向量之间进行转换时。

提供一个清晰简洁的API,能够清晰简洁地表达由向量操作组成的各种向量计算,这些向量计算由循环内的序列组成,并可能带有控制流程。

该API应该与特定的CPU架构无关,能够在支持向量指令的多个架构上实现。

提供在x64和AArch64架构上可靠的运行时编译和性能。

与Valhalla项目保持一致。

在Java16-20中经历了五次孵化

这个还是实验性质的,未来会不会应用尚不好说,毕竟已经经历了五次孵化了。

如果读不懂这个特性,我可以举个很简单的例子,比如你要给一个数组每个元素都+1,首先想到的就是用for循环遍历,对每个元素进行计算操作。那么现在有了向量API,你可以一次对多个元素一起计算,从串行变成并行,效率是不是就提高了。

但需要在具备SIMD(单指令多数据流)能力的机器上,现在Windows一般都是支持的,Linux的话一般也支持,且可以用命令查看:cat /proc/cpuinfo,如果找到类似于ssesse2avx等关键词,说明处理器是支持SIMD指令集的。

所以这个特性在做大规模数学和科学计算时的效率会非常高,可以稍微关注下,估计会继续孵化。

总结

先总结下,Java 21 正式发布后,我的感受是,相比于 Java 17 捆绑式的推广,Java 21 的改进更具有意义。

大家可以重点关注的就是虚拟线程、分代ZGC、作用域、结构化并发这几个,这几个我觉得比较有意义,可能会改变以后Java的并发编程模式。

其他的正式发布的特性,虽然有必要学习,但我觉得更多是功能性的,类似于 Java 8 的 lambada 表达式和 stream 流,依赖于熟能生巧,而且现在有AI辅助,你不会其实也能写。

另外,Spring Boot 3.2 最近也发布了,增加了大量新特性和改进,尤其是对于虚拟线程的支持,所以和 Java 21 算是密不可分。

后面我会单独出一篇文章讲述 Spring Boot 3.2 的版本特性。


如果喜欢,请点赞关注↓↓↓,持续分享干货文章哦!