Подтвердить что ты не робот

Почему 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)
4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

Вот реализация AtomicInteger, предложенная выше erickson. В этом примере 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

Ответ 4

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

static Integer foo = new Integer(1);

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

выход:

Success