S-JIS[2023-09-23] 変更履歴
Javaの仮想スレッドについて。
Java21(プレビュー版ではJava19)で、仮想スレッド(virtual thread)が使えるようになった。
仮想スレッドは、Javaの軽量スレッドである。
仮想スレッドに対し、従来のスレッドのことを「プラットフォームスレッド」と呼ぶ。
プラットフォームスレッドは、OSのスレッドに対応していた(OSのスレッドのラッパーのような実装だった)。
OSスレッドの切り替えにはコストがかかるので、大量のスレッドには向いていない。
仮想スレッドは、プラットフォームスレッド上で仮想スレッドを切り替えて(マウントして)リソースを上手く使ってくれるらしい。
(仮想スレッドの実行時間はプラットフォームスレッドより高速というわけではないので(CPUをフルに使う処理なら同じだろう)、待ちが多くスレッドの切り替え頻度が高い処理に有効らしい)
仮想スレッドも従来のThreadクラスを使用する。
ただし、インスタンスの生成方法が異なる。
(従来のThread(プラットフォームスレッド)のインスタンスの生成方法も、これに合わせて変わる)
仮想スレッドを生成する例。
仮想スレッド | プラットフォームスレッド | |
---|---|---|
Runnable task = () -> { System.out.println("test"); }; |
||
従来の方法 |
var thread = new Thread(task); thread.start(); // スレッド実行開始 thread.join(); // スレッド終了待ち |
|
新しい方法 |
var thread = Thread.ofVirtual().unstarted(task); thread.start(); thread.join(); |
var thread = Thread.ofPlatform().unstarted(task); thread.start(); thread.join(); |
var thread = Thread.ofVirtual().start(task); thread.join(); |
var thread = Thread.ofPlatform().start(task); thread.join(); |
|
var thread = Thread.startVirtualThread(task); thread.join(); |
||
var factory = Thread.ofVirtual().factory(); var thread = factory.newThread(task); thread.start(); thread.join(); |
var factory = Thread.ofPlatform().factory(); var thread = factory.newThread(task); thread.start(); thread.join(); |
Thread.ofVirtual()やThread.ofPlatform()は、Thread.Builderインスタンスを返す。
ビルダーにはいくつか設定できる項目がある。
var builder = Thread.ofVirtual() // or Thread.ofPlatform() .name("thread-name"); var thread = builder.start(task);
分類 | 例 | 備考 |
---|---|---|
共通 | name("name") |
スレッド名。 thread.getName()で取得される。 |
name("name", 0) |
このメソッドでスレッド名を付けると、同じビルダーから複数のスレッドを生成する場合、スレッド名の末尾に連番が付く。 第2引数は連番の初期値(0以上)。 |
|
inheritInheritableThreadLocals(true) |
||
uncaughtExceptionHandler((t, e) ->
{ |
例外ハンドラー。 | |
プラットフォーム | group(threadGroup) |
(ThreadGroupはほとんど使われておらず、非推奨らしい。@Deprecatedは付いてないけど) |
daemon(false) |
(仮想スレッドは常にデーモンスレッドらしい) | |
priority(Thread.MAX_PRIORITY) |
(仮想スレッドの優先度は常にThread.NORM_PRIORITYらしい) | |
stackSize(n) |
Threadクラスに追加されたメソッド。
// 仮想スレッドかどうか boolean b = thead.isVirtual();
ExecutorServiceで仮想スレッドを使う場合は、Executors.newVirtualThreadPerTaskExecutor()を使う。
import java.util.concurrent.Executors;
Runnable task = () -> { System.out.println("test"); }; try (var service = Executors.newVirtualThreadPerTaskExecutor()) { var future = service.submit(task); // 実行開始 future.get(); // 終了待ち }
ThreadFactoryを使う方法もある。
var factory = Thread.ofVirtual().factory(); try (var service = Executors.newThreadPerTaskExecutor(factory)) { 〜 }
従来のスレッド(プラットフォームスレッド)はOSのスレッドなので限りがあり、そのためスレッドをプールするのが有効だったらしい。
(Executors.newFixedThreadPool()ではプールするスレッド数を指定する)
しかし仮想スレッドは軽量なのでプールする必要は無いらしい。
(そもそも仮想スレッドは存在期間が短い処理を想定しており、プールしてはいけないらしい)
仮想スレッドは大量に生成することが出来るので、それをスレッドダンプすると大量になってしまう。
このため、jcmdではjson形式でスレッドダンプを出力できるようになった。
$JAVA_HOME/bin/jcmd プロセスID Thread.dump_to_file -format=json 出力ファイル