0Pricing
Java Academy · レッスン

プラットフォームスレッドと仮想スレッド

それぞれが適する場面を学びます。

「プラットフォームスレッドと仮想スレッド」は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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 仮想スレッドとは
  2. 仮想スレッドの作成
  3. プラットフォームスレッドと仮想スレッド
  4. 落とし穴:ピン留めと ThreadLocal
← Java Academyに戻る