公式リリースの発表: http://jdk.java.net/21/release-notes
オープンソース中国の紹介: https://my.oschina.net/waylau/blog/10112170
新機能の概要:
JEP 431: シーケンス コレクション
JEP 439: 世代別 ZGC
JEP 440: 録音モード
JEP 441: スイッチパターンマッチング
JEP 444: 仮想スレッド
JEP 449: Windows 32 ビット x86 ポートの廃止
JEP 451: プロキシの動的ロードを無効にする準備をする
JEP 452: キーカプセル化メカニズム API
JEP 430: 文字列テンプレート (プレビュー)
JEP 442: 外部関数とメモリ API (第 3 プレビュー)
JEP 443: 名前のないパターンと変数 (プレビュー)
JEP 445: 名前のないクラスおよびインスタンスのメイン メソッド (プレビュー)
JEP 446: スコープ付きの値 (プレビュー)
JEP 453: 構造化同時実行 (プレビュー)
JEP 448: ベクター API (インキュベーター フェーズ 6)
その中でも誰もが注目しているのは世代別の ZGC と仮想スレッドです。
比較17
フレームはステンレススチールからチタンにアップグレードされ、ディレクトリ構造は同じです。
モジュールの数は 17 より 1 つ少ない数です。
全体のサイズが 289MB から 320MB に増加
ダウンロード
アップデートポン
走ってみる
実行中:
java.lang.NoSuchFieldError: クラス com.sun.tools.javac.tree.JCTree$JCImport にはメンバー フィールド 'com.sun.tools.javac.tree.JCTree qualid' がありません
解決策: lombok を 1.18.30 にアップグレードします。
理由: https://github.com/projectlombok/lombok/issues/3393
互換性チェック:
私のプロジェクトでは以前 JDK17 を使用していたので、今回のアップグレードの互換性は良好ですが、
システム トレイで PopupMenu が使用されており、文字セットの問題が発生したことだけがわかりました。
ZGC は以前の JDK バージョンでも利用できましたが、この世代の ZGC はさらに有望です。
世代別 ZGC で実行されるアプリケーションには、次の利点があります。
割り当てが失速するリスクが低くなり、
必要なヒープ メモリのオーバーヘッドが低下し、
ガベージ コレクションの CPU オーバーヘッドが減少します。
コマンド ライン オプション -XX:+UseZGC -XX:+ZGenerational で世代別 ZGC を有効にする
Java21 を使用し、世代別 ZGC を使用する
MooInfo のメモリ使用量ビュー
上記は単なる予備的な体験であり、詳細な世代リサイクルなど、ZGC に関するさらに多くのコンテンツは、将来さらに検討される予定です。
上記のメモリ使用量は以前作成したツール MooInfo を使用して確認しています:
https://github.com/rememberber/MooInfo
仮想スレッドは、高スループットの同時アプリケーションの作成、保守、デバッグの労力を軽減する軽量のスレッドです。
仮想スレッドは、高スループットの同時アプリケーションの作成、保守、デバッグの労力を軽減する軽量のスレッドです。
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。
线程池和所有资源池一样,旨在共享昂贵的资源,
但虚拟线程并不昂贵,而且永远不需要将它们池化。
一个例子感受一下新特性: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);
}
}
本文分享自微信公众号 - 京东云开发者(JDT_Developers)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。