Java 20 中的新特性
1. 概述
Java 20 於 2023 年 3 月 21 日發布,是迄今為止基於 Java 19 構建的最新短期增量版本。它由JEP 2.0中提到的七個重要的 JDK 增強提案 (JEP) 組成。 JEP 流程用於評估 JDK 增強功能的提案。 Java 20 中的大部分更新都是對早期版本中引入的功能的改進或增強。
此外,Oracle JDK 20 不是長期支持版本。因此,它將收到更新,直到 Java 21 發布。
在本文中,我們將探討其中一些新功能。
2. 範圍值 ( JEP 429 )
大量 Java 應用程序都有需要在彼此之間共享數據的組件或模塊。通常,這些模塊是基於線程的,因此我們必須保護它們共享的數據免受任何更改。
我們一直在使用ThreadLocal
類型的變量來允許組件共享數據。
但它會帶來一些後果:
-
ThreadLocal
變量是可變的。ThreadLocal
API 允許訪問其變量類型的get()
和set()
方法。 - 我們可能會面臨內存洩漏問題,因為
ThreadLocal
變量的值會一直保留,直到我們顯式調用它的remove()
方法或線程退出為止。因此,這些每線程變量的生命週期沒有綁定。 - 如果使用大量線程,它們可能會導致內存佔用過多。這是因為子線程可以繼承父線程的
ThreadLocal
變量,從而為每個ThreadLocal
變量分配內存。
為了克服ThreadLocal
變量的這些問題,Java 20 引入了作用域值,用於在線程內和線程間共享數據。
作用域值提供了一個簡單、不可變且可繼承的數據共享選項,特別是在我們使用大量線程的情況下。
ScopedValue
是一個不可變的值,可供線程在有限的執行時間內讀取。由於它是不可變的,因此它允許在有限的線程執行時間內安全且輕鬆地共享數據。此外,我們不需要將值作為方法參數傳遞。
我們可以使用ScopedValue
類的where()
方法來為線程執行的有限週期設置變量的值。而且,一旦我們使用get()
方法獲取了數據,我們就無法再次訪問它。
一旦線程的run()
方法完成執行,作用域值將恢復為未綁定狀態。我們可以使用get()
方法來讀取線程內作用域值變量的值。
3.記錄模式( JEP 432 )
JDK 19 已經引入了記錄模式作為預覽功能。
Java 20 提供了記錄模式的改進和精煉版本。讓我們看看此版本中的一些改進:
- 添加了對通用記錄模式參數的類型推斷的支持。
- 添加了對記錄模式的支持,可在增強的
for
循環的標頭中使用。 - 刪除了對命名記錄模式的支持,我們可以在其中為記錄模式提供一個可選的標識符,我們可以使用它來引用記錄模式。
此版本的本質目的是通過模式匹配來表達更改進的可組合數據查詢,同時不更改類型模式的語法或語義。
4. 開關模式匹配 ( JEP 433 )
Java 20 為switch
表達式和語句提供了模式匹配的改進版本,特別是關於switch
表達式中使用的語法。它首先在 Java 17 中交付,隨後在 Java 18 和 19 中進行了一些改進,從而擴展了switch
語句和表達式的可用性和適用性。
此版本的主要變化包括:
- 現在,在枚舉類上使用
switch
表達式或模式會引發MatchException
。早些時候,如果在運行時沒有應用switch
標籤,我們常常會收到IncompatibleClassChangeError
。 -
switch
標籤的語法有所改進。 - 他們添加了對
switch
表達式和語句中通用記錄模式參數類型推斷的支持,以及支持模式的其他構造。
5. 外部函數和內存 API ( JEP 434 )
Java 20 合併了對先前 Java 版本中引入的外部函數和內存 (FFM) API 的改進和細化。這是第二個預覽版 API 。
外部函數和內存 API 允許 Java 開發人員從 JVM 外部訪問代碼並管理堆外的內存(即不由 JVM 管理的內存)。 FFM API 旨在為 Java 本機接口 (JNI) 提供安全、改進且純基於 Java 的替代品。
它包括以下改進:
-
MemorySegment
和MemoryAddress
抽像是統一的。這意味著我們實際上可以從空間邊界確定與段關聯的內存地址範圍。 - 他們還通過增強密封的
MemoryLayout
層次結構,促進了switch
表達式和語句中模式匹配的使用。 - 此外,他們將
MemorySession
分為Arena
和SegmentScope
,以便於跨維護邊界共享段。
6. 虛擬線程( JEP 436 )
虛擬線程首先作為JEP 425中的預覽功能提出,並在Java 19中提供。 Java 20提出第二個預覽版,旨在收集更多使用後的反饋和改進建議。
虛擬線程是輕量級線程,可以減少編寫、維護和觀察高吞吐量並發應用程序的工作量。因此,使用現有的 JDK 工具可以輕鬆地對服務器應用程序進行調試和故障排除。這對於服務器應用程序的擴展很有用。
然而,我們應該注意到,傳統的Thread
實現仍然存在,並且它的目的並不是要取代Java的基本並發模型。
以下是自第一次預覽以來的一些細微變化:
- 他們使之前在 Thread 中引入的方法 –
[join(Duration)](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/Thread.html#join(java.time.Duration))
、[sleep(Duration)](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/Thread.html#sleep(java.time.Duration))
和threadId()
– 在 Java 20 中永久存在。 - 同樣,
Future
中新引入的用於檢查任務狀態和結果的方法 –state()
和[resultNow()](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Future.html#resultNow())
– 在 Java 20 中已永久保留。 - 此外,
ExecutorService
現在擴展了[AutoCloseable](https://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html)
。 -
ThreadGroup
(JEP 425 中描述的用於分組線程的遺留 API)的降級在 JDK 19 中已永久化[ThreadGroup](https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/ThreadGroup.html)
不適合對虛擬線程進行分組。
7.結構化並發( JEP 437 )
結構化並發由JEP 428提出,並在JDK 19中作為孵化 API提供。該 JEP 建議在 JDK 20 中重新孵化 API,而無需進行太多更改。因此,它可以有時間獲得更多反饋。
目標是通過引入結構化並發 API 來簡化多線程編程。它通過將執行類似任務的多個線程分組到單個工作單元中來提高可靠性。因此,錯誤處理和線程取消得到了改進。此外,它還促進了一種改進的並發編程方式,旨在防止因線程取消而產生的常見風險。
然而,這個重新孵化的 API 有一個變化。我們獲得了更新的StructuredTaskScope
,它支持在任務作用域中創建的線程繼承作用域值。因此,我們現在可以方便地跨多個線程共享不可變數據。
8.矢量API( JEP 438 )
Vector API 最初由JEP 338提出,並作為孵化 API 集成到 JDK 16 中。此版本(第五個孵化器)是多輪孵化和集成到所有先前 Java 版本的後續版本。
Vector API 用於在支持的 CPU 架構上表達 Java 中的向量計算。向量計算實際上是向量上的一系列運算。 Vector API 旨在提供一種比傳統標量計算更優化的矢量計算方法。
與 Java 19 相比,此版本沒有對 API 進行任何更改。但是,它包含一些錯誤修復和性能增強。
最後, Vector
API 的開發與 Valhalla 項目緊密結合,因為它的目標是在未來適應 Valhalla 項目的增強功能。
9. 結論
Java 20 逐步建立在過去版本的多個功能的基礎上,包括記錄模式、 switch
表達式的模式匹配、FFM、虛擬線程等等。它還添加了新的孵化功能(例如作用域值)以及對重新孵化功能(例如結構化並發和 Vector API)的一些增強。
在本文中,我們討論了作為增量 Java 20 版本的一部分引入的一些功能和更改。 Java 20 的完整更改列表位於JDK 發行說明中。