Java
 Computer >> コンピューター >  >> プログラミング >> Java

Javaのwait()・notify()・notifyAll()メソッドとは?スレッド間通信の重要性を解説

Javaでは、wait()notify()notifyAll()という3つのメソッドを使って、スレッド同士が互いに通信(協調動作)を行うことができます。

これらのメソッドには、以下のような重要な特徴があります。

  • Objectクラスで定義されたfinalメソッドであるため、すべてのクラスで利用可能
  • synchronizedブロックまたはsynchronizedメソッドの中からのみ呼び出し可能
  • モニター(ロック)を所有していない状態で呼び出すとIllegalMonitorStateExceptionがスローされる

各メソッドの役割

wait()メソッド

現在実行中のスレッドを待機状態にします。別のスレッドが同じオブジェクトに対してnotify()またはnotifyAll()を呼び出すまで、スレッドは待ち続けます。待機中のスレッドは、オブジェクトのモニター(ロック)を解放する点が重要です。

notify()メソッド

そのオブジェクトのモニター上で待機しているスレッドのうち、1つのスレッドだけを起床(再開)させます。どのスレッドが選ばれるかはJVMの実装に依存します。

notifyAll()メソッド

そのオブジェクトのモニター上で待機しているすべてのスレッドを起床させます。複数のスレッドが同じ条件を待っている場合に適しています。

メソッドの構文

wait()メソッド

public final void wait() throws InterruptedException

notify()メソッド

public final void notify()

notifyAll()メソッド

public final void notifyAll()

実装例:wait()とnotify()を使ったスレッド間通信

次のサンプルコードでは、メインスレッドがjoin()メソッド内でwait()によって待機し、別スレッドが処理完了後にnotify()を呼び出して通知する仕組みを示しています。

public class WaitNotifyTest {
    private static final long SLEEP_INTERVAL = 3000;
    private boolean running = true;
    private Thread thread;

    public void start() {
        print("Inside start() method");
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                print("Inside run() method");
                try {
                    Thread.sleep(SLEEP_INTERVAL);
                } catch(InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                synchronized(WaitNotifyTest.this) {
                    running = false;
                    WaitNotifyTest.this.notify(); // 待機中のスレッドへ通知
                }
            }
        });
        thread.start();
    }

    public void join() throws InterruptedException {
        print("Inside join() method");
        synchronized(this) {
            while(running) {
                print("Waiting for the peer thread to finish.");
                wait(); // runningがfalseになるまで待機
            }
            print("Peer thread finished.");
        }
    }

    private void print(String s) {
        System.out.println(s);
    }

    public static void main(String[] args) throws InterruptedException {
        WaitNotifyTest test = new WaitNotifyTest();
        test.start();
        test.join();
    }
}

実行結果

Inside start() method
Inside join() method
Waiting for the peer thread to finish.
Inside run() method
Peer thread finished.

ポイントまとめ

  • wait()は必ずwhileループと組み合わせて使用するのが推奨されます。これは、スプリアスウェイクアップ(偶発的な起床)や条件の再確認に対応するためです。
  • wait()を呼び出すとモニターのロックが一時的に解放され、notify()によって起床した後、再度ロックを取得して処理を再開します。
  • これらのメソッドを正しく使うことで、生産者・消費者問題のようなスレッド間の協調制御を安全に実装できます。
  1. Javaの同時実行性 – Thread.yield()メソッドの仕組みと使い方

    yield()メソッドとはjava.lang.Thread.yield()メソッドは、現在実行中のスレッドが自発的にCPUの使用権を手放し、より優先度の高い他のスレッドに実行の機会を譲るよう、スレッドスケジューラにヒントを伝えるためのメソッドです。処理に時間がかかっている重要度の低いスレッドよりも、優先して実行すべきスレッドを先に進めたい場面で利用されます。スレッドがyield()を呼び出すと、「自分の実行を一時停止してもよい」という合図がスケジューラに送られます。ただし、この合図を受け入れるか無視するかはスケジューラの判断に委ねられており、必ず譲るとは限らない点に注意が必要です。yield(

  2. Java 9のdestroyForcibly()メソッドとは?プロセスを強制終了する方法を解説

    destroyForcibly() メソッドは、実行中のプロセスを強制的に終了(キル)するために使用されます。このメソッドは、プロセスが正常に終了しない場合や、フリーズ(応答停止)してしまった場合に特に役立ちます。 例えば、destroyForcibly() を呼び出した後でも isAlive() メソッドが true を返すことがあります。これは、OSレベルでの終了処理が完了するまでに時間差があるためです。また、destroyForcibly() は終了要求が正常に受け付けられた場合は true を返し、そうでない場合は false を返します。 構文 boolean destroyForc