Почему notifyAll() вызывает исключение IllegalMonitorStateException при синхронизации с Integer?

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

Решение

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

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

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

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

Увеличение 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

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

static Integer foo = new Integer(1);

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

выход:

успех

Другие вопросы по тегам