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

Javaでロックを取得せずにwait()メソッドを呼び出すことはできる?例外の原因と正しい対処法

いいえロックを取得せずにwait()メソッドを呼び出すことはできません。

Javaにおいて、wait()メソッドはObjectクラスに定義されており、必ず「モニターロック(監視ロック)を保持している状態」で呼び出す必要があります。つまり、対象のオブジェクトに対してsynchronizedブロックまたはsynchronizedメソッド内で、タイムアウトあり・なしのどちらかの形式でwait()を呼び出さなければなりません。

ロックを取得していない状態でwait()メソッドを呼び出そうとすると、実行時にjava.lang.IllegalMonitorStateExceptionがスローされます。なお、同じ仕様はnotify()notifyAll()にも当てはまります。

コード例(誤った使い方)

public class ThreadStateTest extends Thread {
    public void run() {
        try {
            wait(1000);
        } catch(InterruptedException ie) {
            ie.printStackTrace();
        }
    }
    public static void main(String[] s) {
        ThreadStateTest test = new ThreadStateTest();
        test.start();
    }
}

上記の例では、run()メソッド内でロックを一切取得していないにもかかわらずwait()メソッドを呼び出しているため、プログラムの実行時にIllegalMonitorStateExceptionが発生します。

実行結果

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at ThreadStateTest.run(ThreadStateTest.java:4)

問題の解決方法

この問題を解決するには、wait()メソッドを呼び出す前に必ずロックを取得する必要があります。最もシンプルな方法は、run()メソッドをsynchronizedとして宣言することです。

public class ThreadStateTest extends Thread {
    public synchronized void run() { // ロックを取得
        try {
            wait(1000); // 正常に動作する
        } catch(InterruptedException ie) {
            ie.printStackTrace();
        }
    }
}

このように修正すれば、スレッドは自身のインスタンスのロックを保持した状態でwait()を呼び出せるため、例外は発生しなくなります。また、メソッド全体ではなく一部だけを同期化したい場合は、以下のようにsynchronizedブロックを使用することもできます。

synchronized(this) {
    wait(1000);
}

ポイント: wait()・notify()・notifyAll()は、必ずsynchronizedブロックまたはsynchronizedメソッドの中で、ロック対象となっている同じオブジェクトに対して呼び出すというルールを覚えておきましょう。

  1. JavaのSwingUtilities.invokeLater()メソッドの使い方を徹底解説

    invokeLater()メソッドとはinvokeLater()メソッドは、SwingUtilitiesクラスが提供するstaticメソッドの一つで、AWTイベントディスパッチスレッド(Event Dispatch Thread)上でタスクを非同期に実行するために使用されます。このメソッドはSwingUtilities.invokeAndWait()とよく似た動作をしますが、大きな違いは、処理のリクエストをイベントキューに登録した後、すぐに呼び出し元へ制御を返すという点です。つまり、invokeLater()は引数で渡されたRunnableオブジェクト内のコードブロックの実行完了を待ちません。

  2. JavaでJPanelのpaintComponent()メソッドを実装する方法をわかりやすく解説

    JPanelはJavaにおける軽量コンテナであり、それ自体は不可視(インビジブル)なコンポーネントとして扱われます。デフォルトのレイアウトにはFlowLayoutが採用されています。JPanelを生成した後は、Containerクラスから継承されたadd()メソッドを呼び出すことで、ボタンやラベルなどの他のコンポーネントをJPanelオブジェクトに自由に追加できます。 paintComponent()メソッドとは このメソッドは、単なる背景色の描画にとどまらず、JPanel上に独自の図形や画像などを描画したい場合に必要となります。paintComponent()メソッドはすでにJPanelクラ