Por que notifyAll () aumento IllegalMonitorStateException quando sincronizados on Integer?
-
06-07-2019 - |
Pergunta
Por que esse resultado programa de teste em um 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");
}
}
Resultado:
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at test.main(test.java:6)
Solução
Você observou corretamente que notifyAll
deve ser chamado a partir de um bloco sincronizado.
No entanto, no seu caso, por causa da auto-boxing, o objeto que você sincronizado não é a mesma instância que você invocado notifyAll
diante. Na verdade, o novo, exemplo foo
incrementado ainda está confinado à pilha, e não outros tópicos poderia ser bloqueado em uma chamada wait
.
Você poderia implementar seu próprio balcão, mutável em que a sincronização é executada. Dependendo da sua aplicação, você também pode achar que AtomicInteger atenda às suas necessidades.
Outras dicas
Você também deve estar desconfiado de bloqueio ou notificar sobre objetos como cordas e Integer que podem ser internados pela JVM (para evitar a criação de um monte de objetos que representam o número inteiro 1 ou a string "").
Incrementando o Integer faz o velho foo desaparecer e ser substituído por um novo foo objeto marca que não está sincronizada com a variável foo anterior.
Aqui é uma implementação do AtomicInteger que Erickson sugerido acima. Neste exemplo foo.notifyAll (); não produz um Java.lang.IllegalMonitorStateException alta porque o objecto AtomicInteger não é actualizada quando foo.incrementAndGet (); é executado.
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);
}
}
Output:
foo is: 2
Como Erickson fez notar, o código sem que o operador pós-incremento funciona sem erro:
static Integer foo = new Integer(1);
public static void main(String[] args) {
synchronized (foo) {
foo.notifyAll();
}
System.out.println("Success");
}
saída:
Sucesso