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)
Foi útil?

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top