Почему 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)
Ответы
Ответ 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