Почему notifyAll() вызывает исключение IllegalMonitorStateException при синхронизации с целым числом?
-
06-07-2019 - |
Вопрос
Почему эта тестовая программа приводит к 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)
Решение
Вы правильно заметили , что notifyAll
должен быть вызван из синхронизированного блока.
Однако в вашем случае из-за автоматической упаковки объект, с которым вы синхронизировались, не является тем же экземпляром, который вы вызвали notifyAll
вкл.Фактически, новый, увеличенный foo
экземпляр по-прежнему ограничен стеком, и никакие другие потоки не могут быть заблокированы на wait
звони.
Вы могли бы реализовать свой собственный изменяемый счетчик, по которому выполняется синхронизация.В зависимости от вашего приложения вы также можете обнаружить, что Атомарный интегратор отвечает вашим потребностям.
Другие советы
Вы также должны с осторожностью относиться к блокировке или уведомлению таких объектов, как String и Integer, которые могут быть интернированы JVM (чтобы предотвратить создание большого количества объектов, представляющих целое число 1 или строку "").
Увеличение целого числа приводит к исчезновению старого 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
Как отметил Эриксон, код без оператора постинкремента работает без ошибок:
static Integer foo = new Integer(1);
public static void main(String[] args) {
synchronized (foo) {
foo.notifyAll();
}
System.out.println("Success");
}
выходной сигнал:
Успех