HOWTO · Java

Java でスレッド ID を取得

この記事では、Java でスレッドの id を取得する方法を紹介します。

このページの内容

Javaでは、Thread オブジェクトに対して Thread.getId() を呼び出すと、そのスレッドの識別子を取得できます。executorでタスクを実行している場合は、Thread.currentThread().getId() を使うと、そのタスクを実行中のワーカースレッドを識別できます。以下の例では両方の方法を示します。どちらの値も、スレッドプールやスレッド名を識別するものではありません。すでに Thread への参照がある場合は前者を使い、タスク内のコードから現在のワーカーを識別する場合は後者を使います。

Javaで Thread.getId() を使ってスレッドIDを取得する

Thread オブジェクトを作成し、そのオブジェクトの getId() メソッドを呼び出すと、識別子を long 型の値として取得できます。最初の例では2つのスレッドオブジェクトを作成し、それぞれのIDを読み取ります。スレッドは開始しません。これらのオブジェクトの識別子を読み取るために、スレッドを開始する必要はありません。識別子は実行時に割り当てられるため、実行や環境によって異なることがあります。サンプルの数値に頼らず、実行時に返された値を使ってください。特定の Thread への参照を持つコードで、そのオブジェクトを別のオブジェクトと区別したい場合に役立ちます。

public class GetThreadID {
  public static void main(String[] args) {
    Thread thread1 = new Thread();
    Thread thread2 = new Thread();

    System.out.println("Thread 1 ID: " + thread1.getId());
    System.out.println("Thread 2 ID: " + thread2.getId());
  }
}

1回の実行で得られる出力例:

Thread 1 ID: 12
Thread 2 ID: 13

上の数値IDは説明用の例であり、実行時の環境によって変わります。スレッドは開始されていないため、プログラムはprint文の順に2つのオブジェクトIDを出力します。別の実行や環境で同じ数値になるとは限りません。出力例に含まれるラベルは英語のまま表示されます。

Thread.currentThread().getId() でスレッドプール内の現在のワーカーIDを取得する

executorに送信されたタスクは、ワーカースレッドのいずれかで実行されます。タスク内で Thread.currentThread().getId() を呼び出すと、その時点でタスクを実行しているワーカースレッドのIDを取得できます。これはワーカーのIDであり、executorやスレッドプール自体を識別する値ではありません。固定スレッドプールではワーカーが再利用されるため、異なるタスクが同じワーカーIDを返すことがあります。どのワーカーが各タスクを処理するかはスケジューリングによって決まり、タスクの順序も数値IDも変わる場合があります。この違いは、どのスレッドが作業を実行したかを調べるときに重要です。返された数値が示すのは、その時点でタスクを実行していたワーカーであり、タスクを割り当てたexecutorではありません。

この例では5つのタスクを送信し、各タスクの番号とワーカーIDを出力してからexecutorを終了します。shutdown() を呼び出すと、新しいタスクの受け付けは停止しますが、送信済みのタスクは完了できます。awaitTermination はビジースピンせず、最大1分間、完了を待ちます。それより長くかかる場合、プログラムは待機がタイムアウトしたことを出力します。終了要求はすでに行われているため、キューに残った作業はその後も続けられます。本番コードでは、アプリケーションの処理に合ったタイムアウトを選び、時間内にタスクが完了しない場合の対応も決めてください。特定のJavaリリース向けにAPIの詳細を選ぶ必要がある場合は、そのリリースのドキュメントを確認してください。ここでは対象リリースを指定していません。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class GetThreadID {
  public static void main(String[] args) throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(2);

    for (int i = 1; i <= 5; i++) {
      final int taskNumber = i;
      executor.execute(() -> {
        long workerId = Thread.currentThread().getId();
        System.out.println("Task " + taskNumber + " ran on worker ID " + workerId);
      });
    }

    executor.shutdown();
    boolean finished = executor.awaitTermination(1, TimeUnit.MINUTES);
    System.out.println(finished ? "All tasks finished" : "Wait timed out");
  }
}

1回の実行で得られる出力例。タスクの順序と数値IDは実行時の環境によって変わります:

Task 2 ran on worker ID 13
Task 3 ran on worker ID 13
Task 4 ran on worker ID 13
Task 5 ran on worker ID 13
Task 1 ran on worker ID 12
All tasks finished

目的がタスクを実行しているワーカーの特定であれば、呼び出しはタスク内に置いてください。それ以外の場所で呼び出すと、そのコードを実行している別のスレッドが返されます。スレッドプールではワーカーが再利用され、タスクのスケジュールも変わるため、出力は1回の実行を理解するために使い、タスク番号とワーカーIDの固定対応として扱わないでください。出力例に含まれるラベルは英語のまま表示されます。