Thread クラスを継承する方法
class PrimeCounter extends Thread {
@Override
public void run() {
System.out.println("スレッド名: " + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
PrimeCounter counter = new PrimeCounter();
counter.start();
}
Runnable インターフェースを実装する方法
class ImageProcessor implements Runnable {
@Override
public void run() {
System.out.println("実行スレッド: " + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
ImageProcessor task = new ImageProcessor();
Thread worker = new Thread(task);
worker.start();
}
継承方式と実装方式の違い:Thread クラスが Java におけるスレッドの唯一の抽象表現である一方、Runnable は業務ロジックを切り離した独立したタスクとして扱える。Thread は任意の Runnable を受け取り実行できるため、より柔軟な設計が可能。
非同期結果を取得する Future パターン
Thread や Runnable では戻り値を返せないが、Future を使うことでスレッドの実行結果を取得できる。
FutureTaskにCallableを渡してインスタンス化- その
FutureTaskをThreadに渡して起動 Future#get()で結果を取得(ブロッキング)
public static void main(String[] args) throws Exception {
FutureTask<String> task = new FutureTask<>(() -> {
Thread.sleep(2000);
return "処理完了";
});
Thread runner = new Thread(task);
runner.start();
System.out.println(task.get()); // "処理完了"
}
「スレッド生成は何通りあるか?」という設問への答え
結論:公式には 2 通り(Thread 継承、Runnable 実装)。
- Thread コンストラクタを new して start() を呼ぶ以外に、実質的な手段はない。
- Callable + FutureTask は内部で Runnable にラップされるため Runnable 実装と同類。
- ExecutorService などのスレッドプールは「既存スレッドの再利用」であり、新規スレッド生成とは区別される。
start() と run() の挙動差
| メソッド | 意味 | 実行回数 | 内部動作 |
|---|---|---|---|
start() |
新規 OS スレッドを起動し、スケジューラに登録 | 1 回まで(2 回目は IllegalThreadStateException) | JNI の start0() を呼び出す |
run() |
単なるインスタンスメソッドとして実行 | 何度でも呼べる | 通常の Java メソッド呼び出し |
要するに new Thread(...) はあくまで Java オブジェクトであって、start() を叩くことで初めて OS レベルのスレッドが生成される。