IllegalMonitorStateException on wait()コール


161

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


2
Thread.wait()は存在しません。this.wait()の可能性があります
Premraj

回答:


174

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

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

ハッピーコーディング。

編集

Object.wait()例外はオブジェクトのロックを保持せずにアクセスしようとすると何が起こるかということであると私は思っていました。


1
良いキャッチ。私は彼がObject.wait()を意味し、スレッドから呼び出されたと仮定しました
2009年

2
待機しているオブジェクトの同期ブロック。この回答を編集して、もう少し明確にしてください。ありがとう。
灰色

55

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できる限りデータ構造を使用してください。


5
待機と通知の両方が同じオブジェクト(ロック)の同期されたブロック内にあることを考えると、これがどのように機能するかは理解できません。待機スレッドはブロック内にあるので、「同期(ロック)」行で通知スレッドをブロックしてはいけませんか?
Brent212 2014

6
@ Brent212以外のメソッドの場合wait、はい、アクセスできませんnotify。ただし、のAPIドキュメントではObject.wait、「スレッドはこのモニターの所有権を解放します」。そのwaitため、それが囲んでいるsynchronizedブロックの外側にあるかのように見えます(同じオブジェクトの場合、同じオブジェクトに複数のsynchronizedブロックがある場合があります)。
トム・ホーティン-タックライン2014

24

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

違法モニター例外のこの定義を何度も読んでください...

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

この行は何度も何度も言っていますが、IllegalMonitorExceptionは2つの状況のいずれかが発生すると発生します。

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

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

一部の回答が得られた可能性があります...すべてが得られない場合は、2つのステートメントを確認してください

同期(オブジェクト)

object.wait()

両方のオブジェクトが同じ場合...違法監視例外は発生しません。

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


実際には、それは機能しません。私はそれを試しました。Runnableを作成してロックし(同期されたブロックを使用)、そのブロック内でUIスレッド(Android)でRunnableを実行し、その後myRunnable.wait()を実行しても例外が発生します。
2013

エクセレンテ解説!! オブジェクトを指定せずにwait()を実行していたため、インスタンスを取得し、別のオブジェクトで同期しています。今度はotherObject.wait()を使用していて、動作します!
Fersca、2015年

6

あなたのコメントに基づいて、あなたはこのようなことをしているように聞こえます:

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() 、指定されたスレッドを待機させません。むしろそれが原因現在のスレッドが他のスレッドの呼び出しまで待機しますthread.notify()thread.notifyAll()

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

    新しく開始したものThreadを一時停止したい場合は、CountdownLatchインスタンスを作成await()し、ラッチをスレッド呼び出しして一時停止するのが最善の方法です。次に、メインスレッドはcountDown()ラッチを呼び出し、一時停止したスレッドを続行させます。

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

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


2

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

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) { }

    }

@CPerkins:実行スレッドとのターゲットであるオブジェクトを混同していると思いますwait()
ロバートムンテアヌ

@ロバート-多分私はそうですが、私はそうは思いません。スレッドインスタンスを開始して待機するように依頼すると、IllegalMonitorStateExceptionが発生します。これは、私が説明しようとしていたものです。
CPerkins、2009年

あなたはworker.wait()ラインについて話しているのですか?次に、ロックではなくワーカーで同期する必要があります。
Robert Munteanu

1

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

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

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();
        }
    }
}

0

Thread.wait()呼び出しは、Thread.classオブジェクトで同期するコード内で有効です。あなたが言った意味ではないと思います。
あなたが尋ねる

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

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


0

これが他の人を助けるかどうかはわかりませんが、これが上記のユーザー「トム・ホーティン-タックリン」の問題を解決するための重要な部分でした:

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

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

それらの2つの場所で作成したら、それを機能させました


0

IllegalMonitorStateException別のclass/スレッドから/内のスレッドをウェイクアップしようとすると、しばらく待っていました。でjava 8ご使用することができlock、新たな並行処理APIの機能 の代わりsynchronized機能を。

私はすでにasynchronousWebSocketトランザクションのオブジェクトをに格納していましたWeakHashMap。私の場合の解決策は、返信用のオブジェクト格納するlockことConcurrentHashMapsynchronousでした。注意してくださいcondition.await(ではありません.wait)。

マルチスレッドを処理Executors.newCachedThreadPool()するために、スレッドプールを作成するためにを使用しました。


0

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
    }

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.