web-dev-qa-db-ja.com

wait()呼び出しでのIllegalMonitorStateException

私のプログラムでは、Javaでマルチスレッドを使用しています。スレッドを正常に実行しましたが、Thread.wait()を使用しているときに、Java.lang.IllegalMonitorStateExceptionをスローしています。通知されるまでスレッドを待機させるにはどうすればよいですか?

152

Object.wait()が機能するには、synchronizedブロックにいる必要があります。

また、古い学校のスレッドパッケージではなく、同時実行パッケージを確認することをお勧めします。彼らはより安全であり、 扱いやすい です。

ハッピーコーディング。

編集

オブジェクトのロックを保持せずにアクセスしようとすると例外が発生するため、Object.wait()を意味すると想定しました。

163
reccles

waitObjectではなく、Threadで定義されています。 Threadのモニターは少し予測不能です。

すべてのJavaオブジェクトにはモニターがありますが、通常は専用のロックを使用することをお勧めします。

private final Object lock = new Object();

名前付きクラスを使用すると、わずかなメモリコスト(プロセスごとに約2K)で診断をわずかに読みやすくすることができます。

private static final class Lock { }
private final Object lock = new Lock();

オブジェクトをwaitまたはnotify/notifyAllするには、synchronizedステートメントでロックを保持する必要があります。また、ウェイクアップ条件を確認するためにwhileループが必要になります(スレッドについての適切なテキストを見つけて、理由を説明してください)。

synchronized (lock) {
    while (!isWakeupNeeded()) {
        lock.wait();
    }
}

通知するには:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

マルチスレッドを使用する場合は、Java言語とJava.util.concurrent.locksロック(およびJava.util.concurrent.atomic)の両方を理解する価値があります。ただし、できる限りJava.util.concurrentデータ構造を使用してください。

50

私はこのスレッドがほぼ2歳であることを知っていますが、同じ問題でこのQ/Aセッションにも来たので、これを閉じる必要があります...

このillegalMonitorExceptionの定義を何度も読んでください...

IllegalMonitorExceptionは、スレッドがオブジェクトのモニターで待機しようとしたことを示すため、または指定されたモニターを所有せずにオブジェクトのモニターで待機している他のスレッドに通知しようとした場合にスローされます

この行は、2つの状況のいずれかが発生するとIllegalMonitorExceptionが繰り返し発生することを示しています。..

1>指定されたモニターを所有せずにオブジェクトのモニターで待機します。

2>指定されたモニターを所有せずに、オブジェクトのモニターで待機している他のスレッドに通知します。

一部の人は答えを持っているかもしれません...誰もがそうではない場合は、2つのステートメントをチェックしてください....

同期(オブジェクト)

object.wait()

両方のobjectが同じである場合... illegalMonitorExceptionは発生しません。

IllegalMonitorExceptionの定義をもう一度読んで、もう一度忘れないでください...

23
Mina

あなたのコメントに基づいて、次のようなことをしているようです。

Thread thread = new Thread(new Runnable(){
    public void run() { // do stuff }});

thread.start();
...
thread.wait();

3つの問題があります。

  1. 他の人が言ったように、obj.wait()は、現在のスレッドがobjのプリミティブロック/ミューテックスを保持している場合にのみ呼び出すことができます。現在のスレッドがロックを保持していない場合、表示されている例外が発生します。

  2. thread.wait()呼び出しは、期待どおりの動作をしません。具体的には、thread.wait()doesは、指定されたスレッドを待機させます。むしろ、他のスレッドがthread.notify()またはthread.notifyAll()を呼び出すまで、現在のスレッドを待機させます。

    実際には、Threadインスタンスを必要に応じて一時停止させる安全な方法はありません。 (これに最も近いJavaは非推奨のThread.suspend()メソッドですが、Javadocで説明されているように、そのメソッドは本質的に安全ではありません。)

    新しく起動したThreadを一時停止する場合、それを行う最善の方法は、CountdownLatchインスタンスを作成し、ラッチでawait()を呼び出して自分自身を一時停止することです。次に、メインスレッドは、ラッチでcountDown()を呼び出して、一時停止したスレッドを続行します。

  3. Threadオブジェクトをロック/ミューテックスとして使用すると、以前のポイントに直交して問題が発生する場合があります。たとえば、Thread::joinのjavadocは次のように言います。

    この実装は、this.waitを条件とするthis.isAlive呼び出しのループを使用します。スレッドが終了すると、this.notifyAllメソッドが呼び出されます。アプリケーションでは、waitインスタンスでnotifynotifyAll、またはThreadを使用しないことをお勧めします。

5
Stephen C

IllegalMonitorStateExceptionを処理するには、呼び出しスレッドが適切なモニターを所有しているの場合にのみ、wait、notify、およびnotifyAllメソッドのすべての呼び出しが行われることを確認する必要があります。最も簡単な解決策は、これらの呼び出しを同期ブロック内に含めることです。 synchronizedステートメントで呼び出される同期オブジェクトは、モニターを取得する必要があるオブジェクトです。

モニターの概念を理解するための簡単な例を次に示します

public class SimpleMonitorState {

    public static void main(String args[]) throws InterruptedException {

        SimpleMonitorState t = new SimpleMonitorState();
        SimpleRunnable m = new SimpleRunnable(t);
        Thread t1 = new Thread(m);
        t1.start();
        t.call();

    }

    public void call() throws InterruptedException {
        synchronized (this) {
            wait();
            System.out.println("Single by Threads ");
        }
    }

}

class SimpleRunnable implements Runnable {

    SimpleMonitorState t;

    SimpleRunnable(SimpleMonitorState t) {
        this.t = t;
    }

    @Override
    public void run() {

        try {
            // Sleep
            Thread.sleep(10000);
            synchronized (this.t) {
                this.t.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
1

あなたはコードを投稿していないので、私たちは暗闇の中で働いています。例外の詳細は何ですか?

スレッド内またはスレッド外からThread.wait()を呼び出していますか?

IllegalMonitorStateExceptionのjavadocによると、次のとおりだからです。

スレッドがオブジェクトのモニターで待機しようとしたこと、または指定されたモニターを所有せずにオブジェクトのモニターで待機している他のスレッドに通知しようとしたことを示すためにスローされます。

この答えを明確にするために、スレッドを待機するこの呼び出しも、同期ブロック内から呼び出されたにもかかわらず、IllegalMonitorStateExceptionをスローします。


     private static final class Lock { }
     private final Object lock = new Lock();

    @Test
    public void testRun() {
        ThreadWorker worker = new ThreadWorker();
        System.out.println ("Starting worker");
        worker.start();
        System.out.println ("Worker started - telling it to wait");
        try {
            synchronized (lock) {
                worker.wait();
            }
        } catch (InterruptedException e1) {
            String msg = "InterruptedException: [" + e1.getLocalizedMessage() + "]";
            System.out.println (msg);
            e1.printStackTrace();
            System.out.flush();
        }
        System.out.println ("Worker done waiting, we're now waiting for it by joining");
        try {
            worker.join();
        } catch (InterruptedException ex) { }

    }
1
CPerkins

別のIllegalMonitorStateException/スレッドから/にあるスレッドを起動しようとしたときにclassname__を受け取りました。 Java 8では、lockname__関数の synchronizedname__機能の新しい同時実行APIinsteadを使用できます。

asynchronouswebsocketトランザクションのオブジェクトをWeakHashMapname__に既に保存していました。私の場合の解決策は、 lockname__にConcurrentHashMapname__オブジェクトを格納するsynchronousname__返信用にすることでもありました。 condition.await.waitではありません)。

マルチスレッドを処理するには、Executors.newCachedThreadPool()を使用して thread pool を作成しました。

0
Stuart Cardall

Thread.wait()呼び出しは、Thread.classオブジェクトで同期するコード内で意味があります。私はそれがあなたが意味したものだとは思わない。
あなたが尋ねる

通知されるまでスレッドを待機させるにはどうすればよいですか?

現在のスレッドのみを待機させることができます。他のスレッドは、同意する場合にのみ穏やかに待機するように要求できます。
何らかの条件を待ちたい場合、ロックオブジェクトが必要です-Thread.classオブジェクトは非常に悪い選択です-それはシングルトンのAFAIKであるため、(Thread静的メソッドを除く)同期は危険です。
同期と待機の詳細については、トム・ホーティンがすでに説明しています。 Java.lang.IllegalMonitorStateExceptionは、同期されていないオブジェクトで待機しようとしていることを意味します。そうすることは違法です。

0
Tadeusz Kopec

Java 7.0以前のバージョンを使用している人は、ここで使用したコードを参照できます。

public class WaitTest {

    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void waitHere(long waitTime) {
        System.out.println("wait started...");
        lock.lock();
        try {
            condition.await(waitTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
        System.out.println("wait ends here...");
    }

    public static void main(String[] args) {
        //Your Code
        new WaitTest().waitHere(10);
        //Your Code
    }

}
0
mannedear

これが他の誰かを助けるかどうかはわかりませんが、これは上記のユーザー「Tom Hawtin-tacklin」の答えの私の問題を解決する重要な部分でした:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

「ロック」がsynchronized()の引数として渡され、「ロック」.notifyAll()でも使用されるという事実だけです。

これらの2か所で作成したら、機能しました

0
jp093121