Javaスレッドのライフサイクルと状態遷移の詳細解析

Javaスレッドの基本状態とその特徴

Javaのスレッドはjava.lang.Thread.State列挙型で定義される6つの状態を持ち、それぞれが異なる実行フェーズを表しています。

状態名 説明 遷移条件
CREATED スレッドインスタンス生成後、start()メソッド呼び出し前 new Thread(runnable)
RUNNING_READY CPU時間割当待ちまたは実行中の状態 thread.start()呼び出し後
BLOCKED_ON_MONITOR synchronizedロック取得待ちによるブロッキング状態 他スレッドがロック保持中のコード領域へのアクセス時
WAITING_INDEFINITE 明確な解除トリガーが必要な無期限待機状態 Object.wait(), Thread.join()呼び出し時
WAITING_TIMED 時間制限付きの待機状態 Thread.sleep(), Object.wait(timeout)呼び出し時
FINISHED 実行終了または例外発生による終了状態 run()メソッド完了時

状態遷移の詳細メカニズム

各状態間の遷移は特定の条件下でのみ発生し、理解することで並列処理の問題解決に役立ちます。

  • CREATED → RUNNING_READY: start()メソッド呼び出しによりシステムリソースが割り当てられ、実行可能状態に移行
  • RUNNING_READY → BLOCKED_ON_MONITOR: synchronized保護されたリソースへのアクセス時に他スレッドのロック解放待ち
  • RUNNING_READY → WAITING_INDEFINITE: wait()やjoin()メソッド呼び出しによる待機状態への移行
  • RUNNING_READY → WAITING_TIMED: sleep()などの時間指定待機メソッド呼び出し
  • BLOCKED_ON_MONITOR → RUNNING_READY: 目的のロックを取得した際に発生
  • WAITING系 → RUNNING_READY: notify()や時間経過、join対象の完了などによって解除

重要な設計原則と落とし穴

状態遷移に関する重要な考慮事項:

  1. 一度終了したスレッドは再起動不可能
  2. WAITING状態からの解除はロック再取得プロセスを含む
  3. WAITINGとBLOCKEDの違いを正しく理解する必要がある
  4. 古くなったstop()メソッドは使用しない

実践的な診断手法とツール

実行中のアプリケーションでスレッド状態を確認するための方法:

// コード内での状態確認
Thread targetThread = new Thread(() -> {
    // 処理内容
});
targetThread.start();
Thread.State currentState = targetThread.getState();

// コマンドラインでの診断
// jpsコマンドでPID確認
// jstack <pid>で全スレッド状態をダンプ

面接向けの応用質問と回答戦略

高度な理解を問う質問に対する回答例:

Q: notify()呼び出し後のスレッド動作について説明してください。
A: notify()によってWAITING状態のスレッドが即座に実行されるわけではありません。まずBLOCKED状態に移行し、以前wait()で解放したロックを再取得する必要があります。ロック獲得後にのみRUNNING_READY状態に戻り、実行可能になります。

Q: sleep()とwait()の主な違いは何ですか?
A: sleep()はロックを保持したまま時間待機し、wait()はロックを解放して待機します。また、wait()はsynchronizedブロック内でしか呼び出せません。

並列処理設計における考慮事項

スレッド状態の理解は以下の設計要素に影響します:

  • リソース競合の検出と軽減
  • パフォーマンスボトルネックの特定
  • デッドロックの予防と検出
  • 適切なスレッドプールサイズの決定

wait/notifyメカニズムの詳細理解

スレッド間協調のための基本的な同期プリミティブの仕組み:

役割 メソッド 目的 後続処理
待機側 wait() 条件不成立時の待機とロック解放 通知後ロック再取得、条件再評価
通知側 notify() 条件変更後の他スレッド起床 ロック保持継続、同期ブロック完了まで

これらのメソッドはすべてsynchronizedコンテキスト内で呼び出す必要があります。条件の検証にはifではなくwhileループを使用し、spurious wakeupに対応することが推奨されます。

実装例:プロデューサ・コンシューマパターン

public class ProducerConsumerExample {
    private final Object monitor = new Object();
    private final java.util.Queue<String> buffer = new java.util.LinkedList<>();
    private final int capacity = 5;

    public void produce(String item) throws InterruptedException {
        synchronized (monitor) {
            while (buffer.size() == capacity) {
                monitor.wait(); // バッファフル時の待機
            }
            buffer.add(item);
            System.out.println("Produced: " + item);
            monitor.notifyAll(); // 待機中のコンシューマを通知
        }
    }

    public String consume() throws InterruptedException {
        synchronized (monitor) {
            while (buffer.isEmpty()) {
                monitor.wait(); // バッファ空時の待機
            }
            String item = buffer.poll();
            System.out.println("Consumed: " + item);
            monitor.notifyAll(); // 待機中のプロデューサを通知
            return item;
        }
    }
}

タグ: Java Multithreading thread-state concurrency synchronization

5月27日 23:38 投稿