プラットフォームスレッドと仮想スレッド
それぞれが適する場面を学びます。
「プラットフォームスレッドと仮想スレッド」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
2種類のスレッド
Java には現在、2種類のスレッドがあります。プラットフォームスレッドは、従来の OS スレッドをラップしたものです。仮想スレッドは軽量で、JVM によって管理されます。
どちらを使うべきかを理解することは、スケーラブルな並行 Java プログラムを書くうえで中心となる考え方です。
プラットフォームスレッドを作成する
Thread.ofVirtual() に対応する Thread.ofPlatform() を使うと、プラットフォームスレッドを作成できます。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread p = Thread.ofPlatform().name("native-worker").start(() ->
System.out.println("Platform? " + !Thread.currentThread().isVirtual()));
p.join();
}
}コストの比較
プラットフォームスレッドは高コストです。
- 1つあたり約1 MB のスタックを予約する
- 作成に時間がかかる
- 数千個程度に制限される
仮想スレッドは低コストです。初期スタックは小さく、作成も高速で、数百万個の利用も可能です。
I/Oバウンドの処理
タスクの大半がネットワーク、ディスク、データベースの待機に費やされる場合、仮想スレッドは大きな効果を発揮します。
1つの仮想スレッドがブロックしている間、そのキャリアは解放されて別の処理を実行できるため、少数の OS スレッドで数千のリクエストを処理できます。
CPUバウンドの処理
一度もブロックしない純粋な計算処理では、仮想スレッドによる高速化はありません。コア数による制約を受けます。
この場合は、プロセッサ数に合わせた固定サイズのプールが適しています。
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws Exception {
int cores = Runtime.getRuntime().availableProcessors();
try (ExecutorService cpu = Executors.newFixedThreadPool(cores)) {
Future<Long> f = cpu.submit(() -> {
long s = 0;
for (long i = 0; i < 1_000_000L; i++) s += i;
return s;
});
System.out.println("Cores: " + cores + ", sum: " + f.get());
}
}
}ブロッキングをシミュレーションする
このコードは、多数の仮想スレッドが同時にスリープする様子を示しています。これはブロッキング I/O の代わりとなる処理です。10,000 個のプラットフォームスレッドで同じことを行う場合を想像してみてください。
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) {
AtomicInteger done = new AtomicInteger();
try (ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
exec.submit(() -> {
try { Thread.sleep(50); } catch (InterruptedException e) {}
done.incrementAndGet();
});
}
}
System.out.println("Finished blocking tasks: " + done.get());
}
}デーモン状態
仮想スレッドは常にデーモンスレッドであり、非デーモンスレッドにすることはできません。プラットフォームスレッドはデフォルトでは作成元のデーモン状態を継承しますが、明示的に設定することもできます。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread v = Thread.ofVirtual().unstarted(() -> {});
Thread p = Thread.ofPlatform().unstarted(() -> {});
System.out.println("virtual daemon: " + v.isDaemon());
System.out.println("platform daemon: " + p.isDaemon());
}
}優先度は無視される
仮想スレッドが報告する優先度は常に NORM_PRIORITY であり、優先度を設定しても効果はありません。スケジューリングは OS のスケジューラではなく JVM が処理します。
public class Main {
public static void main(String[] args) {
Thread v = Thread.ofVirtual().unstarted(() -> {});
v.setPriority(Thread.MAX_PRIORITY);
System.out.println("Priority is still: " + v.getPriority());
}
}選択の指針
実践的な目安は次のとおりです。
- I/O の待機が多いタスクには、仮想スレッドを使用します。
- CPU によって制約される重い計算には、プラットフォームスレッドの固定サイズプールを使用します。
- OS レベルでスレッドを調整する必要がある場合(優先度、アフィニティなど)は、プラットフォームスレッドを使用します。
共存できる
プログラム全体でどちらか一方だけを選ぶ必要はありません。一般的なアプリケーションでは、リクエスト処理に仮想スレッドを使用し、CPU 負荷の高い部分には小規模なプラットフォームスレッドプールを使用します。
どちらも同じ Thread API を共有しているため、シームレスに組み合わせられます。
移行のヒント
I/O 負荷の高いサービスを移行する場合、最小限の変更はエグゼキュータを置き換えることです。固定プールまたはキャッシュされたプールを newVirtualThreadPerTaskExecutor() に置き換えます。
ただし、次のレッスンで扱うピン留めと ThreadLocal の使いすぎには注意してください。
理解度チェック
仮想スレッドによる効果が最も明確に現れるワークロードを選んでください。
復習
2つのスレッドモデルを比較しました。
- プラットフォームスレッド:高コストで OS に支えられており、CPUバウンドの処理や OS レベルの調整に適している
- 仮想スレッド:低コストで JVM によって管理され、常にデーモンであり、I/Oバウンドの同時実行に適している
- どちらも共存でき、同じ API を共有する
次は、ピン留めと ThreadLocal の落とし穴を学びます。
よくある質問
「プラットフォームスレッドと仮想スレッド」レッスンは無料ですか?
はい。「プラットフォームスレッドと仮想スレッド」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「プラットフォームスレッドと仮想スレッド」で何を学びますか?
それぞれが適する場面を学びます。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「プラットフォームスレッドと仮想スレッド」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 仮想スレッドとは
- 仮想スレッドの作成
- プラットフォームスレッドと仮想スレッド
- 落とし穴:ピン留めと ThreadLocal