web-dev-qa-db-ja.com

Integerで同期するとnotifyAll()がIllegalMonitorStateExceptionを発生させるのはなぜですか?

このテストプログラムはなぜJava.lang.IllegalMonitorStateException

public class test {
    static Integer foo = new Integer(1);
    public static void main(String[] args) {
        synchronized(foo) {
            foo++;
            foo.notifyAll();
        }
        System.err.println("Success");
    }
}

結果:

Exception in thread "main" Java.lang.IllegalMonitorStateException
        at Java.lang.Object.notifyAll(Native Method)
        at test.main(test.Java:6)
39
jjvainio

notifyAllは同期ブロックから呼び出す必要があることを正しく説明しました。

ただし、あなたの場合、自動ボックス化のため、同期したオブジェクトはnotifyAllを呼び出したインスタンスと同じではありません。実際、インクリメントされた新しいfooインスタンスはまだスタックに限定されており、wait呼び出しで他のスレッドをブロックすることはできません。

同期が実行される独自の可変カウンタを実装できます。アプリケーションによっては、 AtomicInteger がニーズを満たす場合もあります。

55
erickson

また、JVMがインターンできるStringやIntegerなどのオブジェクトをロックまたは通知しないようにする必要があります(整数1または文字列 ""を表す多くのオブジェクトが作成されるのを防ぐため)。

3
dbt

Integerをインクリメントすると、古いfooが消えて、以前のfoo変数と同期していない新しいオブジェクトfooに置き換えられます。

以下は、エリクソンが上記で提案したAtomicIntegerの実装です。この例では、foo.notifyAll(); foo.incrementAndGet();の場合、AtomicIntegerオブジェクトは更新されないため、Java.lang.IllegalMonitorStateExceptionは生成されません。実行されます。

import Java.util.concurrent.atomic.AtomicInteger;

public class SynchronizeOnAPrimitive {
    static AtomicInteger foo = new AtomicInteger(1);
    public static void main(String[] args) {
        synchronized (foo) {
            foo.incrementAndGet();
            foo.notifyAll();
        }
        System.out.println("foo is: " + foo);
    }
}

出力:

foo is: 2
3
Eric Leschinski

エリクソンが指摘したように、ポストインクリメント演算子のないコードはエラーなしで機能します。

static Integer foo = new Integer(1);

public static void main(String[] args) {
    synchronized (foo) {
        foo.notifyAll();
    }
    System.out.println("Success");
}

出力:

成功

1
matt b