Почему notifyAll () вызывает IllegalMonitorStateException при синхронизации с Integer? - PullRequest
38 голосов
/ 04 ноября 2008

Почему эта тестовая программа дает 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)

Ответы [ 4 ]

54 голосов
/ 04 ноября 2008

Вы правильно заметили, что notifyAll должен вызываться из синхронизированного блока.

Однако, в вашем случае, из-за автобокса объект, на котором вы синхронизировались, не тот экземпляр, на котором вы вызывали notifyAll. Фактически, новый инкрементный экземпляр foo все еще ограничен стеком, и никакие другие потоки не могут быть заблокированы при вызове wait.

Вы можете реализовать свой собственный изменяемый счетчик, на котором выполняется синхронизация. В зависимости от вашего приложения вы также можете обнаружить, что AtomicInteger соответствует вашим потребностям.

3 голосов
/ 19 марта 2011

Увеличение значения Integer приводит к исчезновению старого foo и замене его новым объектом foo, который не синхронизирован с предыдущей переменной foo.

Вот реализация AtomicInteger, предложенная Эриксоном выше. В этом примере foo.notifyAll (); не создает java.lang.IllegalMonitorStateException, поскольку объект AtomicInteger не обновляется, когда foo.incrementAndGet (); выполняется.

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 голосов
/ 04 ноября 2008

Вам также следует опасаться блокировки или уведомления о таких объектах, как String и Integer, которые могут быть интернированы JVM (чтобы предотвратить создание большого количества объектов, представляющих целое число 1 или строку "").

1 голос
/ 04 ноября 2008

Как заметил Эриксон, код без оператора postincrement работает без ошибок:

static Integer foo = new Integer(1);

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

выход:

Успех

...