[编程开发] Java 21 正式发布,该版本有哪些功能?

[复制链接]
xjhtbyz 发表于 2023-12-3 22:38:30|来自:英国 | 显示全部楼层 |阅读模式
9 月 19 日,以六个月为更新频次的 Java 正式发布了最新的长期支持版本——Java 21,这一版本将获得官方 5 年重要的支持,这意味着对于开发者、企业而言,如果想要用上稳定又前沿的 Java,JDK 21 无疑是最好的选择。

在最新的 Java 21 版本中, Oracle 开发团队为其带来了 15 大功能更新,详细如下:
全部回复6 显示全部楼层
mingren119 发表于 2023-12-3 22:39:11|来自:英国 | 显示全部楼层
前言

首先,感谢一下不少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和腾讯等公司的开发人员做出的杰出贡献。
以及来自Bellsoft和Loongson等小型组织以及独立开发人员的贡献,他们总共贡献了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找同名目录下载即可:
[烟男/JavaShare]


发布特性

正式发布的特性,都是未来需要领悟的特性,类似于 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,如果找到类似于sse, sse2, avx等关键词,说明处理器是支持SIMD指令集的。
所以这个特性在做大规模数学和科学计算时的效率会非常高,可以稍微关注下,估计会继续孵化。
总结

先总结下,Java 21 正式发布后,我的感受是,相比于 Java 17 捆绑式的推广,Java 21 的改进更具有意义。
大家可以重点关注的就是虚拟线程、分代ZGC、作用域、结构化并发这几个,这几个我觉得比较有意义,可能会改变以后Java的并发编程模式。
其他的正式发布的特性,虽然有必要学习,但我觉得更多是功能性的,类似于 Java 8 的 lambada 表达式和 stream 流,依赖于熟能生巧,而且现在有AI辅助,你不会其实也能写。
另外,Spring Boot 3.2 最近也发布了,增加了大量新特性和改进,尤其是对于虚拟线程的支持,所以和 Java 21 算是密不可分。
后面我会单独出一篇文章讲述 Spring Boot 3.2 的版本特性。
<hr/>如果喜欢,请点赞关注↓↓↓,持续分享干货文章哦!
qijian 发表于 2023-12-3 22:39:56|来自:英国 | 显示全部楼层
JDK21可能是java世界近10年(也可能是20年)最重要的更新,虚拟线程的出现让各类java的开发模式都会发生变化,几乎接近一种新语言。
本文讨论在虚拟线程的原理以及其对gc的影响。
虚拟线程

先测试一下:

Thread.ofVirtual().start(() -> {
  System.out.println("virtual");
}).join();如果在空项目中测试,一定记得join
日常代码中怎么创建虚拟线程

Thread.ofVirtual().start(() -> System.out.println("virtual"));像golang使用go一样,切记不要像过去一样使用线程池,虚拟线程并不需要复用。
虚拟线程执行在什么真实线程中?

在java.lang.VirtualThread代码中可以看到,最终还是
return new ForkJoinPool(parallelism, factory, handler, asyncMode,
                         0, maxPoolSize, minRunnable, pool -> true, 30, SECONDS);因此在jvm层面只能看到几个ForkJoinPool,数量由Runtime.getRuntime().availableProcessors()或-Djdk.virtualThreadScheduler.parallelism决定
虚拟线程的本质是什么?

在同个线程中顺序的执行代码,当遇到io密集任务时,交出控制权到其他代码中,因此本质上来说:

  • 看起来像是并发,体验也是并发,实际并不是(在同个进程中时)
  • 对cpu密集型运算没有任何帮助,对于对业务开发更常见的io密集型有很大帮助
  • 因为在同个线程中,没有对栈内存的使用,都在使用堆内存,因此对gc有更大的压力
  • 极大的降低了开发及debug成本
虚拟线程+gc

虚拟线程把其栈放在堆中,不像普通线程放在栈中。大量创建线程会产生cpu压力,而大量创建虚拟线程会产生gc压力,虽然每个虚拟线程实际上只有一个java.lang.Thread对象+部分信息的大小(几百bytes)。
测试方法:
使用更偏向io密集的方式进行测试,1000并发在堆内创建大量的对象。只有nio比较能接近虚拟线程的场景,1000个普通线程在真实场景中几乎不可能出现。
因此使用1000个mono循环调用自身 vs 1000个虚拟线程while(true)死循环运行,两者背后都是真实的10个线程,http get请求一个1M的文件,放在本机的nginx下。1m的String对象在大并发的情况下会给gc造成很大压力,以此来测试gc性能。
非常简单的虚拟线程代码:
IntStream.range(0, 1000).forEach(j -> Thread.ofVirtual().start(() -> {
    while(true) {
        http.Do(j);
    }
}));比较晦涩的reactor代码:
public void run(String... args) throws Exception {
        IntStream.range(0, 1000).forEach(i -> Do());
    }

    void Do(){
        webClient.get().uri("/1m.json").retrieve().bodyToMono(String.class).doOnSuccess((s) -> {
            Do();
        }).subscribe();
    }java21最新的分代zgc

使用参数:-XX:+UseZGC -XX:+ZGenerational -Xmx4g
zgc是没有年轻代老年代的,因此它的gc动作是发生在所有堆内存上的。分代zgc把分代这个传统模式又加回了zgc,毕竟再快的gc,运行在更小的范围中也会更快。


⬆️ 虚拟线程


⬆️ reactor
可以看到reactor模式的内存明显要低大概30%,虚拟线程还是比较耗费内存
普通zgc



⬆️ 虚拟线程


⬆️ reactor
可以看到reactor模式的内存会稍微低一些,但不太明显。这也是为什么分代zgc会在将来全面替换普通zgc的原因,分代后效率还是更高
默认的g1



⬆️ 虚拟线程


⬆️ reactor
g1似乎非常不适应虚拟线程,几乎满载的状态,很担心快要oom了。使用虚拟线程一定不要使用g1
结论:

虚拟线程使用要点:

  • 不要使用线程池
  • 尽量不要使用ThreadLocal。虽然可以正常运行,但创建太快又不能复用,会给ThreadLocal造成很大压力。这里golang的channel是比较好的方案,希望java世界在虚拟线程的普及中也有类似的方案出来
  • 不要使用synchronized,因为其会锁住真实的ForkJoinPool造成协程调度失败。使用ReentrantLock
  • 必须使用分代zgc,否则gc压力比过去大很多
jdk 8-17的gc对比:
a
胖六传奇 发表于 2023-12-3 22:40:19|来自:英国 | 显示全部楼层
根据规划,Java 21 是长期支持版本

Oracle 将至少提供 8 年支持。根据客户反馈和 Java 生态系统使用情况,Oracle 还宣布将 Java 11 的支持至少延长至 2032 年 1 月,也就是至少再提供 8 年的支持与更新。
从 Java 11 到 Java 21 的正式发布,一共修复了 24,196 个 JIRA 问题,其中 17,288 个是由 Oracle 的工作人员完成的,而 6,908 个是由个人开发人员和其他组织的开发人员贡献的。
从下图可以看到,中国的腾讯、阿里巴巴、华为和龙芯做出了不少贡献。


盘点 Java 21 的新特性

除了大量的性能、稳定性和安全性方面的更新,Java 21 还包括 15 个 JEP(JDK Enhancement Proposal),其中包括 6 个预览特性和 1 个孵化特性。
例如;Java 21 新特性 - 虚拟线程

1. 什么是Virtual Threads?

Virtual Threads(虚拟线程)是Java平台的一项新功能,它旨在改进Java中的并发编程模型。传统上,Java使用基于操作系统线程的并发模型,每个线程都需要分配一个操作系统线程来执行。而Virtual Threads则提供了一种更高效、更轻量级的线程模型。
2. 为什么需要Virtual Threads?

在传统的基于操作系统线程的并发模型中,创建和销毁线程以及在线程之间切换的开销很大。这限制了Java应用程序在处理大规模并发时的性能和扩展性。此外,由于操作系统线程的数量有限,当应用程序需要创建大量线程时,可能会导致资源耗尽或者性能下降。
Virtual Threads的出现解决了这些问题。它通过引入一种轻量级的线程模型,可以在Java应用程序中创建数百万甚至数十亿个线程,而不会受到操作系统线程数量的限制。这使得Java应用程序能够更好地适应大规模并发场景,并提供更高的性能和可伸缩性。
3. Virtual Threads的实现原理

Virtual Threads的实现依赖于Java虚拟机(JVM)的协作调度器和Fork/Join框架。它通过将多个Virtual Threads映射到少量的操作系统线程上来实现高效的并发执行。
具体而言,当一个Java应用程序创建一个Virtual Thread时,JVM会为其分配一个虚拟线程(也称为轻量级线程)。这些虚拟线程由协作调度器管理,并在需要时与操作系统线程进行绑定。协作调度器负责决定哪个虚拟线程可以运行以及何时切换虚拟线程。
Fork/Join框架是Virtual Threads的另一个关键组件。它提供了一种任务并行编程模型,允许开发人员将任务分解成更小的子任务,并使用工作窃取算法来实现负载均衡。Virtual Threads利用Fork/Join框架的能力,在不同的虚拟线程之间自动地、透明地进行任务划分和调度。
4. Virtual Threads的优点


  • 更高的性能:Virtual Threads减少了线程创建和销毁的开销,同时避免了操作系统线程数量的限制,从而提供更高的性能。
  • 更好的可伸缩性:由于Virtual Threads可以创建数百万甚至数十亿个线程,因此Java应用程序可以更好地适应大规模并发场景,并具有更好的可伸缩性。
  • 更低的资源消耗:相比于操作系统线程,Virtual Threads是轻量级的,占用更少的内存和CPU资源。
5. Virtual Threads的缺点

虽然Virtual Threads带来了许多优势,但也存在一些潜在的缺点:

  • 学习成本较高:使用Virtual Threads需要对并发编程模型有一定的理解,并且需要适应新的API和开发范式。
  • 可能引入新的问题:由于Virtual Threads是一个相对较新的功能,可能会存在一些未知的问题或者不稳定性。
6. Virtual Threads的使用示例

下面是一个简单的使用Virtual Threads的示例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class VirtualThreadsExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newVirtualThreadExecutor();

        for (int i = 0; i < 10; i++) {
            int taskId = i;
            executor.execute(() -> {
                System.out.println("Task " + taskId + " is running on virtual thread: " + Thread.currentThread().getName());
            });
        }

        executor.shutdown();
    }
}

上述示例中,我们使用Executors.newVirtualThreadExecutor()方法创建了一个ThreadPoolExecutor实例,该实例可以执行Virtual Threads。然后,我们通过调用execute()方法提交了一系列任务,每个任务都会打印当前运行的虚拟线程的名称。
7. Virtual Threads的使用注意事项

在使用Virtual Threads时,需要注意以下几点:

  • 虽然Virtual Threads可以创建大量线程,但过多的线程仍可能导致性能下降或资源耗尽。因此,在设计应用程序时,仍需合理控制并发度。
  • 使用Virtual Threads时,需要遵循良好的并发编程实践,如避免共享可变状态、使用适当的同步机制等,以确保线程安全性和正确性。
  • 在迁移现有代码到使用Virtual Threads时,需要进行一定的重构和调整,以适应新的API和开发范式。
8. 总结

Virtual Threads是Java平台的一项新功能,旨在改进Java中的并发编程模型。它通过引入轻量级的虚拟线程,并利用协作调度器和Fork/Join框架来提供高效的并发执行。Virtual Threads具有更高的性能、更好的可伸缩性和较低的资源消耗,但也需要学习成本较高,并且可能存在一些潜在的问题。在使用Virtual Threads时,需要注意合理控制并发度、遵循并发编程实践,并进行必要的重构和调整。
推荐一些,学习专栏:JDK 7-JDK 21 新特性汇总

Java 21 新特性


  • 01、Java 21 新特性 - 序列集合
  • 02、Java 21 新特性 - 分代 ZGC
  • 03、Java 21 新特性 - 记录模式
  • 04、Java 21 新特性 - switch 模式匹配
  • 05、Java 21 新特性 - 虚拟线程
  • 06、Java 21 新特性 - 弃用 Windows 32 位 x86 移植
  • 07、Java 21 新特性 - 准备禁止动态加载代理
  • 08、Java 21 新特性 - 密钥封装机制 API
  • 09、Java 21 新特性 - 字符串模板(预览)
  • 10、Java 21 新特性 - 外部函数和内存 API(第三次预览)
  • 11、Java 21 新特性 - 未命名模式和变量(预览)
  • 12、Java 21 新特性 - 未命名类和实例主方法(预览)
  • 13、Java 21 新特性 - 作用域值(预览)
  • 14、Java 21 新特性 - 结构化并发(预览)
  • 15、Java 21 新特性 - Vector API(孵化器第六阶段)
Java 7 新特性

01、 Java 7 新特性 - Switch中添加对String类型的支持
02、 Java 7 新特性 - 数字字面量的改进
03、 Java 7 新特性 - 异常处理(捕获多个异常) try-with-resources
04、 Java 7 新特性 - 增强泛型推断
05、 Java 7 新特性 - NIO2.0(AIO)新IO的支持
06、 Java 7 新特性 - JSR292与InvokeDynamic
07、 Java 7 新特性 - Path接口(重要接口更新)
08、 Java 7 新特性 - fork/join计算框架
Java 8 新特性

01、 Java 8 新特性 - 收集器 – java.util.stream.Collectors;
02、 Java 8 新特性 - IntStream,LongStream,DoubleStream;
03、 Java 8 新特性 - Collectors.joining() 详解;
04、 Java 8 新特性 - Runnable Lambda 表达式;
05、 Java 8 新特性 - java.util.Base64 编码解码;
06、 Java 8 新特性 - Lambda 表达式 ( 上 )- 简介;
07、 Java 8 新特性 - Lambda 表达式 ( 中 )- 外部参数;
08、 Java 8 新特性 - Lambda 表达式 ( 下 )范例;
09、 Java 8 新特性 - 方法引用;
10、 Java 8 新特性 - 接口 ( interface ) 默认方法;
11、 Java 8 新特性 - 接口静态方法;
12、 Java 8 新特性 - 集合遍历 forEach() 方法;
13、 Java 8 新特性 - 可选值 java.util.Optional 类;
14、 Java 8 新特性 - Nashorn JavaScript;
15、 Java 8 新特性 - 新日期时间 API ( 上 ) – 本地日期时间;
16、 Java 8 新特性 - 新日期时间 API ( 中 ) – 时区日期时间;
17、 Java 8 新特性 - 新日期时间 API ( 下 ) – 格式化;
18、 Java 8 新特性 - 流 Stream ( 上 );
19、 Java 8 新特性 - 流 Stream ( 下 );
20、 Java 8 新特性 - 函数接口 ( Functional interface );
Java 9 新特性

01、 Java 9 新特性 – 介绍;
02、 Java 9 新特性 – 特性简介;
03、 Java 9 新特性 – 环境配置;
04、 Java 9 新特性 – 模块化;
05、 Java 9 新特性 – REPL ( JShell );
06、 Java 9 新特性 – 改进 JavaDocs;
07、 Java 9 新特性 – 多版本共存 JAR;
08、 Java 9 新特性 – 集合不可变实例工厂方法;
09、 Java 9 新特性 – 接口 ( interface ) 的私有方法;
10、 Java 9 新特性 – 改进进程管理 API;
11、 Java 9 新特性 – 增强流 ( Stream ) API;
12、 Java 9 新特性 – try-with-resources 语句;
13、 Java 9 新特性 – 增强 @Deprecated 注解;
14、 Java 9 新特性 – 内部类的方块操作符;
15、 Java 9 新特性 – Option 类;
16、 Java 9 新特性 – 多分辨率图像 API;
17、 Java 9 新特性 – CompletableFuture API ( 上 );
18、 Java 9 新特性 – CompletableFuture API ( 中 );
19、 Java 9 新特性 – CompletableFuture API ( 下 );
20、 Java 9 新特性 – 其它特性;
Java 10 新特性

01、 Java10 新特性 - 介绍;
02、 Java10 新特性 - 基于时间的发布版本控制;
03、 Java10 新特性 - 局部变量类型推断;
04、 Java10 新特性 - 新的API 和选项;
05、 Java10 新特性 - 删除的功能和选项;
06、 Java10 新特性 - 已弃用的功能和选项;
07、 Java10 新特性 - JIT编译器;
08、 Java10 新特性 - 应用程序类数据共享;
09、 Java10 新特性 - 增强的垃圾收集;
10、 Java10 新特性 - Unicode语言标签扩展;
11、 Java10 新特性 - 堆分配;
12、 Java10 新特性 - JDK多个代码仓库合并;
13、 Java10 新特性 - 根证书;
14、 Java10 新特性 - 线程本地握手;
Java 11 新特性

01、 Java11 新特性 - 介绍;
02、 Java11 新特性 - 标准HttpClient;
03、 Java11 新特性 - 免编译启动;
04、 Java11 新特性 - 增强String的API;
05、 Java11 新特性 - 集合转换为数组;
06、 Java11 新特性 - 文件API;
07、 Java11 新特性 - Optional类;
08、 Java11 新特性 - Predicate接口;
09、 Java11 新特性 - Lambda中使用var;
10、 Java11 新特性 - 嵌套类的使用;
11、 Java11 新特性 - 删除-弃用的 API;
Java 12 新特性

01、 Java12 新特性 - 介绍;
02、 Java12 新特性 - Switch表达式;
03、 Java12 新特性 - File的mismatch方法;
04、 Java12 新特性 - getCompactNumberInstance方法;
05、 Java12 新特性 - teeing方法;
06、 Java12 新特性 - String的新方法;
07、 Java12 新特性 - 垃圾收集增强;
Java 13 新特性

01、 Java13 新特性 - 介绍;
02、 Java13 新特性 - Switch表达式;
03、 Java13 新特性 - 文本块;
04、 Java13 新特性 - 文本块方法;
05、 Java13 新特性 - Socket API;
06、 Java13 新特性 - 动态CDS存档;
07、 Java13 新特性 - ZGC增强;
08、 Java13 新特性 - 其他变化;
Java 14 新特性

01、 Java14 新特性 - 介绍;
02、 Java14 新特性 - Switch表达式;
03、 Java14 新特性 - 文本块;
04、 Java14 新特性 - instanceof的模式匹配;
05、 Java14 新特性 - NullPointerException;
06、 Java14 新特性 - 打包工具;
07、 Java14 新特性 - NUMA;
08、 Java14 新特性 - 其他增强功能;
09、 Java14 新特性 - 弃用和删除功能;
Java 15 新特性

01、 Java15 新特性 - 介绍;
02、 Java15 新特性 - sealed密封类;
03、 Java15 新特性 - instanceof的模式匹配;
04、 Java15 新特性 - 文本块;
05、 Java15 新特性 - Record的sealed密封类;
06、 Java15 新特性 - Hidden隐藏类;
07、 Java15 新特性 - 垃圾收集器;
08、 Java15 新特性 - 其他增强功能;
09、 Java15 新特性 - 弃用和删除的功能;
10、 Java15 新特性 - 其他变化;
Java 16 新特性

01、 Java16 新特性 - 介绍;
02、 Java16 新特性 - sealed密封类;
03、 Java16 新特性 - instanceof的模式匹配;
04、 Java16 新特性 - ValueBased类警告;
05、 Java16 新特性 - Record类;
06、 Java16 新特性 - 打包工具;
07、 Java16 新特性 - 垃圾收集器;
08、 Java16 新特性 - 弃用和删除的功能;
09、 Java16 新特性 - 其他增强功能;
Java 17 新特性

01、 Java 17 新特性 - 文本块
02、 Java 17 新特性 - switch表达式
03、 Java 17 新特性 - record关键字
04、 Java 17 新特性 - 密封类 sealed class
05、 Java 17 新特性 - instanceof模式匹配
06、 Java 17 新特性 - Helpful NullPointerExceptions
07、 Java 17 新特性 - 日期周期格式化
08、 Java 17 新特性 - 精简数字格式化支持
09、 Java 17 新特性 - Stream.toList()
10、 Java 17 新特性 - 新的macOS渲染管道
11、 Java 17 新特性 - 增强型伪随机数生成器
12、 Java 17 新特性 - ZGC 的改进
13、 Java 17 新特性 - Unicode 13 的支持
14、 Java 17 新特性 - 针对嵌入式系统的改进
15、 Java 17 新特性 - 生成的代码的性能改进
16、 Java 17 新特性 - HTTP 客户端改进
Java 18 新特性

01、 Java 18 新特性 - 默认 UTF-8 字符编码
02、 Java 18 新特性 - 简单的 Web 服务器
03、 Java 18 新特性 - Javadoc 中支持代码片段
04、 Java 18 新特性 - 使用方法句柄重新实现反射核心功能
05、 Java 18 新特性 - Vector API(三次孵化)
06、 Java 18 新特性 - 互联网地址解析 SPI
07、 Java 18 新特性 - Foreign Function & Memory API (第二次孵化)
08、 Java 18 新特性 - switch 表达式(二次孵化)
09、 Java 18 新特性 - 弃用删除相关
Java 19 新特性

01、 Java 19 新特性 - HashMap 新的构造方法
02、 Java 19 新特性 - switch 类型匹配增强(第三次预览)
03、 Java 19 新特性 - record 的匹配增强(预览功能)
04、 Java 19 新特性 - 虚拟线程(预览功能)
05、 Java 19 新特性 - Foreign Function 和 Memory API (预览功能)
06、 Java 19 新特性 - 结构化并发(孵化器)
07、 Java 19 新特性 - Vector API(第四次预览)
08、 Java 19 新特性 - 弃用和删除的一些 API
Java 20 新特性

01、 Java 20 新特性 - switch 模式匹配(第四预览版)
02、 Java 20 新特性 - Record 模式(第二预览版)
03、 Java 20 新特性 - 虚拟线程(第二个预览版)
04、 Java 20 新特性 - 作用域值 Scoped Value(孵化器)
05、 Java 20 新特性 - 结构化并发(第二个孵化器版本)
06、 Java 20 新特性 - 外部函数 & 内存 API(第二个预览版)
07、 Java 20 新特性 - Vector API(第五个孵化器版本)
Java 21 新特性

01、 Java 21 新特性 - 序列集合
02、 Java 21 新特性 - 分代 ZGC
03、 Java 21 新特性 - 记录模式
04、 Java 21 新特性 - switch 模式匹配
05、 Java 21 新特性 - 虚拟线程
06、 Java 21 新特性 - 弃用 Windows 32 位 x86 移植
07、 Java 21 新特性 - 准备禁止动态加载代理
08、 Java 21 新特性 - 密钥封装机制 API
09、 Java 21 新特性 - 字符串模板(预览)
10、 Java 21 新特性 - 外部函数和内存 API(第三次预览)
11、 Java 21 新特性 - 未命名模式和变量(预览)
12、 Java 21 新特性 - 未命名类和实例主方法(预览)
13、 Java 21 新特性 - 作用域值(预览)
14、 Java 21 新特性 - 结构化并发(预览)
15、 Java 21 新特性 - Vector API(孵化器第六阶段)

再推荐一些,高质量,学习资料

最新,高清 7701页,大厂面试题 PDF
大厂,牛逼的,程序员简历模板。下载
Java 离线版 LeetCode 累计 3000+ 刷题笔记
Java,Spring,SpringBoot,SpringCloud 学习路线
bebe861011 发表于 2023-12-3 22:40:31|来自:英国 | 显示全部楼层
Java 21 / JDK 21 已正式 GA,此版本是继JDK 17后的长期支持版本 (LTS),Oracle 将为其提供至少八年的技术支持和更新。


正式稳定功能

  • JEP 444:虚拟线程
  • JEP 431:序列集合
  • JEP 440:记录模式
  • JEP 441:switch 模式匹配
  • JEP 439:分代 ZGC
  • JEP 449:弃用 Windows 32 位 x86 移植
  • JEP 451:准备禁止动态加载代理
  • JEP 452:密钥封装机制 API
预览阶段功能

  • JEP 442:外部函数和内存 API(第三次预览)
  • JEP 446:作用域值(预览)
  • JEP 453:结构化并发(预览)
  • JEP 430:字符串模板(预览)
  • JEP 443:未命名模式和变量(预览)
  • JEP 445:未命名类和实例主方法(预览)
  • 面试跳槽在Java面试库小程序
孵化阶段功能

  • JEP 448:Vector API(孵化器第六阶段)
<hr/>
430:String Templates (Preview)
431:Sequenced Collections
439:Generational ZGC
440:Record Patterns
441:Pattern Matching for switch
442:Foreign Function & Memory API (Third Preview)
443:Unnamed Patterns and Variables (Preview)
444:Virtual Threads
445:Unnamed Classes and Instance Main Methods (Preview)
446:Scoped Values (Preview)
448:Vector API (Sixth Incubator)
449:Deprecate the Windows 32-bit x86 Port for Removal
451:Prepare to Disallow the Dynamic Loading of Agents
452:Key Encapsulation Mechanism API
453:Structured Concurrency (Preview)
Download: https://jdk.java.net/21/
Release notes: https://jdk.java.net/21/release-notes
Features: https://openjdk.org/projects/jdk/21/
buxizhizhou 发表于 2023-12-3 22:41:26|来自:英国 | 显示全部楼层
重磅好消息:从今年年初就一直在喊的具有革命性、未来性、开创新纪元的 JDK 21 终于是要正式 GA 了:
JDK 21


GA,就是我上面框起来的“General Availability”的缩写,直译成中文,虽然是“普通可用”的意思,但是在软件行业,它就代表正式版。
如果对外发布一个 GA 版本,就意味着这个版本已经经过全面的测试,不存在任何重大的 bug,可供普通用户进行使用。
既然说到 GA 了,歪师傅也顺便给大家普及一下一般我们看到的版本号的含义。
比如我们经常会看到一些软件发布的时候都会带上 Alpha、Beta、Gamma、RC 等等这些莫名其妙的单词,它们代表什么意思呢?

  • Alpha:软件或系统的内部测试版本,仅内部人员使用。一般不向外部发布,通常会有很多 Bug,除非你也是测试人员,否则不建议使用,alpha 就是 α,是希腊字母的第一位,表示最初级的版本,beta 就是 β,alpha 版就是比 beta 还早的测试版,一般都是内部测试的版本。
  • Beta:公开测试版。β 是希腊字母的第二个,顾名思义,这一版本通常是在 Alpha 版本后,该版本相对于 Alpha 版已有了很大的改进,消除了严重的错误,但还是存在着一缺陷,需要经过多次测试来进一步消除。这个阶段的版本会一直加入新的功能。
  • Gamma: 软件或系统接近于成熟的版本,只需要做一些小的改进就能发行。是 beta 版做过一些修改,成为正式发布的候选版本。
  • RC:Release Candidate,发行候选版本。和 Beta 版最大的差别在于 Beta 阶段会一直加入新的功能,但是到了 RC 版本,几乎就不会加入新的功能了,而主要着重于除错。RC 版本是最终发放给用户的最接近正式版的版本,发行后改正 bug 就是正式版了,就是正式版之前的最后一个测试版。
  • GA:General Available,正式发布的版本,这个版本就是正式的版本。在国外都是用 GA 来说明 release 版本的。比如:MySQL Community Server 5.7.21 GA 这是 MySQL Community Server 5.7 第 21 个发行稳定的版本,GA 意味着 General Available,也就是官方开始推荐广泛使用了。
  • Release:这个版本通常就是所谓的“最终版本”,在前面版本的一系列测试版之后,终归会有一个正式版本,是最终交付用户使用的一个版本,该版本有时也称为标准版。一般情况下,Release 不会以单词形式出现在软件封面上,取而代之的是符号(R)。
  • Stable:稳定版。在开源软件中,都有 stable 版,这个就是开源软件的最终发行版,用户可以放心大胆的用了。这一版本基于 Beta 版,已知 Bug 都被修复,一般情况下,更新比较慢。
除了上面的这些之外,我们还经常看见一个 LTS 的版本号。
LTS,Long Term Support,长期支持版,是指针对软件的某一版本,提供长时间的技术支持、安全更新和错误修复。
相对于非 LTS 版本,LTS 版本被认为是更为稳定、可靠和安全的版本。因此,在需要稳定性和安全性较高的场景中,如生产环境、企业级应用等,LTS 版本得到广泛的应用。
在 Java 领域,LTS 版本是指 Oracle 公司发布的 Java SE(Standard Edition,标准版)中,每隔一段时间发布一个长期支持版本。
自 2018 年开始,Oracle Java SE 8 、Java SE 11、Java SE 17 成为了 LTS 版本,分别提供了 3 年、 8 年、至少 3 年的支持。
你看,一个小小的 GA 里面,隐藏了这么多的小知识点,让歪师傅一不小心就铺(水)垫(了)这么长。
回到 JDK 21 今天的 GA 版本,一共发布了 15 个新特性:




一眼望去,其中最扎眼的,也是描述最短的一个 Feature 是 444 号 Virtual Threads:
JEP 444: Virtual Threads


可以说这个特性就是 JDK 21 这个版本中最受瞩目、最值得期待的一个特性了。
Virtual Threads,就是虚拟线程,从 JDK 19 吆喝到 JDK 20,终于在 JDK 21 现真身了。
前面我形容 JDK 21 的时候提到了一个词:开创新纪元。
值得就是它,根据官方介绍,虚拟线程的出现,确实是开启了并发编程的新纪元,轻量且高效,用更少的开销,处理更多的任务。
最重要的是看看这个:


minimal,就是小小的也很可爱,就是“极小的”。change,就是变化。
官方表示,使用 java.lang.Thread API 的现有代码,只需做 极少改动(minimal change)即可启用虚拟线程。
至少到你升级到 JDK 21 之后,只需要把创建线程池的地方修改为这样就能启用虚拟线程:




至于这个玩意到底有多牛逼,你可以随便在网上检索一下,已经有很多相关的文章了。
但是我还是建议你直接看官方的这个描述,这才是第一手资料出现的地方,如果让我来描述,我也只能是对于官方文章进行拙劣的翻译。
JEP 444: Virtual Threads
除了 444 之外,也有其他的很多实用的特性。
根据官方的信息,它们把这 15 个新特性按照 JEP 的形式分为四类:核心 Java 库,Java 语言规范,HotSpot 和安全库。
JDK 21 and JDK 22: What We Know So Far


纳入核心 Java 库的 6 个新特性分别是:

  • JEP 431:序列集合
  • JEP 442:外部函数和内存 API(第三次预览)
  • JEP 444:虚拟线程
  • JEP 446:作用域值(预览)
  • JEP 448:Vector API (第六次孵化器)
  • JEP 453:结构化并发(预览)
纳入 Java 语言规范的 5 个新特性分别是:

  • JEP 430:字符串模板(预览)
  • JEP 440:记录模式
  • JEP 441:switch 模式匹配
  • JEP 443:未命名模式和变量(预览)
  • JEP 445:未命名类和实例主方法(预览)
纳入 HotSpot 的 3 个新特性分别是:

  • JEP 439:分代 ZGC
  • JEP 449:弃用 Windows 32 位 x86 移植
  • JEP 451:准备禁止动态加载代理
纳入安全库的 1 个新特性是:

  • JEP 452:密钥封装机制 API
其中 445 号提案,也小小的火了一把,因为它被大多数网友调侃为“卵用不大”。
因为这个提案是为了简化 Hello World 的写法。
445:        Unnamed Classes and Instance Main Methods (Preview)


在这个提案中,作者认为我们的 Hello World 太复杂了,要写这么多代码:
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}所以他提议,为了 students 和 beginner 更快更好的入门 Java,应该简化成这样:


但是这个提案也强调了:这是预览语言功能,默认禁用。


如果你要用,需要这样操作:

  • 用 javac --release 21 --enable-preview Main.java 编译程序,用 java --enable-preview Main 运行程序。
  • 或者,当使用源代码启动器 source code launcher,时,用 java --source 21 --enable-preview Main.java 运行程序。
对于这个功能,怎么说的?这很难评。最后,再说一下编号为 439 的提案。
JEP 439: Generational ZGC


ZGC,大家不陌生了吧?
这个提案提到它是干啥呢?
目前的版本中,ZGC 都是不分代 GC 的。在 JDK 21 的版本中,提供了分代 GC 的功能,但是默认是关闭状态,需要通过配置打开:


而且,注意最后这句话:
In a future release we intend to make Generational ZGC the default, at which point -XX:-ZGenerational will select non-generational ZGC. In an even later release we intend to remove non-generational ZGC, at which point the ZGenerational option will become obsolete.
在未来的版本中,官方会把 ZGenerational 设为默认值,即默认打开 ZGC 的分代 GC。
在更晚的版本中,官方会考虑删除 ZGC 的不分代 GC 功能,到时候 ZGenerational 这个配置就会被一并“优化”。
说到这个 ZGC 的分代 GC,我突然想起了一个有意思的问题。
就是 2018 年,在 JDK 11 的里面,刚刚开始宣传 ZGC 的时候,就有人问:ZGC 为什么不进行分代啊?有大佬答道:


没有什么特别的原因,就是“因为分代实现起来麻烦,想先实现出比较简单可用的版本”而已。
这句话,像不像我们常常听到的:先跑起来再说?
但是人家的“跑起来再说”和我们的“跑起来再说”完全不是一个维度的东西。
至少,人家提供的“跑起来”的版本,从 2018 年跑到了 2023 年,5 年时间。
而你的“跑起来再说”可能一周后就接到了全新的、不兼容的需求。
5 年就更别想了,业务可能都被砍了,跑不起来了。(手动狗头)
既然都提到了 ZGC 了,那就顺便提一嘴 Shenandoah 吧。
根据官方的消息,最开始 JDK 21 的版本中是包含了 Shenandoah 的。
JEP 404: Generational Shenandoah (Experimental)


但是为什么 GA 版本中没有看到它的影子呢?
可以看看官方 6 月份的这个邮件:
JEP proposed to drop from JDK 21: 404: Generational Shenandoah (Experimental)


别问,问就是时间紧,任务重,申请延期。
首先描述了原因:
Given the risks identified during the review process and the lack of time available to perform the thorough review that such a large contribution of code requires
因为 Shenandoah 在审查的过程中发现了明确的风险,并且没有足够的时间来进行针对大量代码改动所需的评审。
然后画了一个新饼:
take the time to deliver the best Generational Shenandoah that we can.
Shenandoah 团队决定“尽他们所能提供最好的分代 Shenandoah”。
并给自己设置了一个新任务,将 JDK 22 作为发布目标:
We will seek to target JDK 22.
能怎么办,等着呗。
反正你发任你发,我用 Java 8!
Viktorirrv 发表于 2023-12-4 18:06:46|来自:英国 | 显示全部楼层

Where is Administrator jinriwenda.com ??

bebe861011 ??? 2023-12-3 22:40
Java 21 / JDK 21 ??? GA,?????JDK 17???????? (LTS),Oracle ???????????? ...

Where is moderator??
It is about advertisement on your website.
Thank.

快速回帖

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则