Th-01: Thread の基本
Java でスレッドを起動する方法は大きく2つあります。Thread クラスを継承する方法と、Runnable インターフェースを実装して Thread に渡す方法です。 それぞれの使い方と、start() / run() の違い、join() による待機を理解しましょう。
Thread クラス継承 vs Runnable 実装
スレッドの処理内容を定義するには2つの方法があります。Runnable インターフェースを実装する方法が推奨です。 Java は単一継承(1つのクラスしか継承できない)のため、Thread を継承してしまうと他のクラスを継承できなくなります。Runnable はインターフェースなので、既存の継承関係に影響しません。
| 方法 | 書き方 | 推奨度 | 備考 |
|---|---|---|---|
| Thread クラスを継承 | class MyThread extends Thread | △ | 他のクラスを継承できなくなる |
| Runnable を実装 | class MyTask implements Runnable | ◎ | 柔軟性が高く、ExecutorService にも渡せる |
| ラムダ式(Java 8+) | new Thread(() -> { ... }) | ◎ | 短い処理に向く。Runnable を匿名で書ける |
start() と run() の違い
スレッドを起動するには必ず start() を呼びます。run() を直接呼ぶと、新しいスレッドは起動されずメインスレッドで処理が実行されます。
Thread t = new Thread(task); t.start(); // ✅ 新しいスレッドで run() を実行 t.run(); // ❌ メインスレッドで run() を実行(スレッドが増えない)
join() による完了待機
join() を呼ぶと、そのスレッドが終了するまでメインスレッドが待機します。join() なしで処理を続けると、スレッドの完了前にプログラムが終了してしまう場合があります。
サンプルコード
よくあるミス・注意点
⚠️ thread.run() を呼んでしまう
run() を直接呼ぶと新しいスレッドは起動されません。メインスレッド上でそのまま実行されるため、 並行処理にならず想定外の動作になります。スレッドを起動するときは必ず start() を使ってください。
⚠️ join() を忘れてメインスレッドが先に終了する
スレッドを起動した後に join() を呼ばないと、スレッドの処理が完了する前にメインスレッドが終了してしまうことがあります。 スレッドの結果を使う処理の前に必ず t.join() を呼びましょう。
⚠️ InterruptedException を握りつぶす
Thread.sleep() などが投げる InterruptedException を catch した後、何もしないまま続けるのは危険です。Thread.currentThread().interrupt() を呼んで interrupt フラグを再セットしてください。 これをしないと、外部からのスレッド中断シグナルが失われます。
テストする観点
- ✅
start()を呼ぶと新しいスレッドが起動され、Thread.currentThread().getName()がメインスレッドと異なること - ✅
run()を直接呼んだ場合、スレッド名がメインスレッドと同じであること(シングルスレッドで実行されている) - ✅
join()を呼ぶとスレッドの処理が完全に終了してから次の行が実行されること - ✅
InterruptedExceptionを catch した後、isInterrupted()がtrueであること - ✅ Java 21 では
Thread.ofVirtual().start()で起動したスレッドのisVirtual()がtrueであること