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

Javaのwait()メソッドとwait(long)メソッドの違いを徹底解説!使い分けのポイント

Javaのマルチスレッドプログラミングにおいて、wait()メソッドはオブジェクトに対して呼び出されると、現在実行中のスレッドを待機状態にし、別のスレッドが同じオブジェクトに対してnotify()またはnotifyAll()メソッドを呼び出すまで待ち続けます。

一方、wait(long timeout)メソッドは、notify()やnotifyAll()が呼び出されるか、あるいは指定されたタイムアウト時間が経過するまでのどちらか早い方で、スレッドが待機状態から復帰する点が大きな違いです。

wait()メソッドの動作

以下のプログラムでは、オブジェクトに対してwait()を呼び出すと、スレッドは「実行可能(Runnable)状態」から「待機(Waiting)状態」へ遷移します。この状態では、他のスレッドがnotify()またはnotifyAll()を呼び出してくれるのをひたすら待つことになります。もし誰も通知を行わなければ、スレッドは永久に待機し続け、デッドロックが発生する可能性があります。

サンプルコード

class MyRunnable implements Runnable {
    public void run() {
        synchronized(this) {
            System.out.println("In run() method");
            try {
                this.wait();
                System.out.println("Thread in waiting state, waiting for some other threads on same object to call notify() or notifyAll()");
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        }
    }
}
public class WaitMethodWithoutParameterTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable, "Thread-1");
        thread.start();
    }
}

実行結果

In run() method

この出力結果からわかるように、「In run() method」の後に続くメッセージは表示されません。これは、wait()によってスレッドが待機状態に入り、notify()やnotifyAll()を呼び出すスレッドが存在しないため、処理がそこで止まったままになっているからです。

wait(long)メソッドの動作

次のプログラムでは、オブジェクトに対してwait(1000)を呼び出しています。この場合もスレッドは「実行可能状態」から「待機状態」へ遷移しますが、たとえnotify()やnotifyAll()が呼び出されなくても、指定したタイムアウト時間(この例では1000ミリ秒=1秒)が経過すれば、スレッドは自動的に「待機状態」から「実行可能状態」へ戻ります。

サンプルコード

class MyRunnable implements Runnable {
    public void run() {
        synchronized(this) {
            System.out.println("In run() method");
            try {
                this.wait(1000);
                System.out.println("Thread in waiting state, waiting for some other threads on same object to call notify() or notifyAll()");
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        }
    }
}
public class WaitMethodWithParameterTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable, "Thread-1");
        thread.start();
    }
}

実行結果

In run() method
Thread in waiting state, waiting for some other threads on same object to call notify() or notifyAll()

今回はwait(1000)にタイムアウトが設定されているため、1秒後にスレッドが自動的に復帰し、wait()の後続の処理であるメッセージ出力が実行されています。

注意点:synchronizedブロック内でのみ呼び出し可能

wait()およびwait(long)は、どちらもsynchronizedブロックまたはsynchronizedメソッドの中で、そのオブジェクトのモニターロックを保持している状態でしか呼び出せません。ロックを保持していない状態で呼び出すと、IllegalMonitorStateExceptionがスローされます。これは両メソッドに共通する重要なルールなので、覚えておきましょう。

まとめ

  • wait():notify()またはnotifyAll()が呼ばれるまで無期限に待機する。通知がないとデッドロックの危険がある。
  • wait(long):notify()/notifyAll()が呼ばれるか、指定時間が経過するまで待機する。タイムアウト付きなので安全に待機を解除できる。

無限待機によるデッドロックを避けたい場合は、タイムアウト付きのwait(long)を使うのが実践的です。状況に応じて適切なメソッドを選択しましょう。

  1. Javaのpack()メソッドとは?使い方とsetSize()との違いを解説

    pack()メソッドは、JavaのWindowクラスで定義されているメソッドで、フレーム内のすべてのコンポーネントがそれぞれの推奨サイズ(preferred size)以上で表示されるように、ウィンドウのサイズを自動的に調整します。 代替手段として、setSize()メソッドやsetBounds()メソッドを呼び出して、フレームのサイズを明示的に指定する方法もあります。しかし一般的には、pack()メソッドを使用する方がsetSize()よりも推奨されています。その理由は、pack()がフレームのサイズ決定をレイアウトマネージャーに委ねることができ、レイアウトマネージャーはプラットフォーム間の

  2. JavaでThread.start()の代わりにThread.run()を呼び出すのはどんなとき?違いを徹底解説

    スレッドに対してstart()メソッドを呼び出すと、そのスレッドの実行が開始され、run()メソッドはJava仮想マシン(JVM)によって呼び出されます。一方、run()メソッドを直接呼び出した場合は、Threadクラス(またはRunnableインターフェース)の通常のオーバーライドメソッドとして扱われ、新しいスレッドではなく現在のスレッドのコンテキスト内で実行されるという重要な違いがあります。 start()とrun()の違い start()メソッドrun()メソッド 新しいスレッドを作成し、そのスレッド上でrun()を実行する現在のスレッド上で通常のメソッドとして実行される JVMに