IllegalMonitorStateException na chamada wait ()

162

Estou usando o multi-threading em java para o meu programa. Eu executei o thread com êxito, mas quando estou usando Thread.wait(), ele está sendo lançado java.lang.IllegalMonitorStateException. Como posso fazer com que um thread aguarde até ser notificado?

prakash.panjwani
fonte
2
Thread.wait () não existe, pode ser this.wait ()
Premraj

Respostas:

175

Você precisa estar em um synchronizedbloco para Object.wait()poder trabalhar.

Além disso, recomendo examinar os pacotes de simultaneidade em vez dos pacotes de threading da velha escola. Eles são mais seguros e fáceis de trabalhar .

Feliz codificação.

EDITAR

Supus que você quis dizer Object.wait()que sua exceção é o que acontece quando você tenta obter acesso sem manter o bloqueio de objetos.

reccles
fonte
1
boa pegada. Presumi que ele significava Object.wait () e chamado a partir de um fio
reccles
2
Um bloco sincronizado no objeto que você está esperando. Gostaria de editar esta resposta para deixar isso um pouco mais claro? Obrigado.
Grey
55

waité definido em Object, e não ele Thread. O monitor ligado Threadé um pouco imprevisível.

Embora todos os objetos Java tenham monitores, geralmente é melhor ter um bloqueio dedicado:

private final Object lock = new Object();

Você pode obter um pouco mais fácil de ler os diagnósticos, com um pequeno custo de memória (cerca de 2 K por processo) usando uma classe nomeada:

private static final class Lock { }
private final Object lock = new Lock();

Para / waitou um objeto, você precisa estar segurando a trava com a instrução Além disso, você precisará de um loop para verificar a condição de ativação (encontre um bom texto sobre o encadeamento para explicar o porquê).notifynotifyAllsynchronizedwhile

synchronized (lock) {
    while (!isWakeupNeeded()) {
        lock.wait();
    }
}

Para notificar:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

Vale a pena entender a linguagem Java e os java.util.concurrent.locksbloqueios (e java.util.concurrent.atomic) ao entrar no multithreading. Mas use java.util.concurrentestruturas de dados sempre que puder.

Tom Hawtin - linha de orientação
fonte
5
Eu nunca entendi como isso funciona, já que a espera e a notificação estão em blocos sincronizados no mesmo objeto (bloqueio). Como o segmento de espera está no bloco, isso não deve impedir o segmento de notificação na linha "sincronizada (bloqueio)"?
Brent212
6
@ Brent212 Para qualquer outro método wait, sim, você nunca chegaria a isso notify. No entanto, nos documentos da API para Object.wait"O encadeamento libera a propriedade deste monitor". Portanto, enquanto waitestá como se estivesse fora dos synchronizedblocos anexos (para o mesmo objeto, pode haver vários synchronizedblocos no mesmo objeto).
Tom Hawtin - defina
24

Eu sei que este tópico tem quase 2 anos, mas ainda preciso encerrar isso, pois também participei dessa sessão de perguntas e respostas com o mesmo problema ...

Por favor, leia esta definição de ilegalMonitorException repetidamente ...

IllegalMonitorException é acionada para indicar que um encadeamento tentou aguardar no monitor de um objeto ou para notificar outros encadeamentos que aguardam no monitor de um objeto sem possuir o monitor especificado.

Essa linha diz repetidamente que IllegalMonitorException ocorre quando ocorre uma das 2 situações ....

1> aguarde no monitor de um objeto sem possuir o monitor especificado.

2> notificar outros threads que aguardam no monitor de um objeto sem possuir o monitor especificado.

Alguns podem ter suas respostas ... quem não sabe, por favor, verifique duas instruções ....

sincronizado (objeto)

object.wait ()

Se ambos os objetos forem iguais ... então nenhuma ilegalMonitorException poderá aparecer.

Agora leia novamente a definição IllegalMonitorException e você não a esquecerá novamente ...

Mina
fonte
Na verdade, isso não funciona. Eu tentei. Eu crio um Runnable, trava nele (usando bloco sincronizado) e dentro desse bloco eu executo Runnable no thread da interface do usuário (Android) e depois faço myRunnable.wait () e ainda recebo a exceção.
Ted
Excelente explicação !! Eu estava fazendo wait () sem especificar o objeto, por isso levou a instância e sincronizou com outro objeto. Agora estou usando otherObject.wait () e funciona!
Fersca
6

Com base nos seus comentários, parece que você está fazendo algo parecido com isto:

Thread thread = new Thread(new Runnable(){
    public void run() { // do stuff }});

thread.start();
...
thread.wait();

Existem três problemas.

  1. Como outros já disseram, obj.wait()só pode ser chamado se o encadeamento atual contiver o bloqueio / mutex primitivo para obj. Se o encadeamento atual não reter o bloqueio, você obtém a exceção que está vendo.

  2. A thread.wait()chamada não faz o que você espera. Especificamente, thread.wait() não faz com que o segmento indicado aguarde. Em vez disso, faz com que o encadeamento atual aguarde até que outro encadeamento chame thread.notify()outhread.notifyAll() .

    Na verdade, não há maneira segura de forçar uma Threadinstância a pausar, se ela não quiser. (O Java mais próximo disso é o Thread.suspend()método obsoleto , mas esse método é inerentemente inseguro, conforme explicado no Javadoc.)

    Se você deseja que o recém-iniciado faça Threaduma pausa, a melhor maneira de fazer isso é criar uma CountdownLatchinstância e fazer com que a chamada de encadeamento await()na trava seja pausada. O encadeamento principal chamaria countDown()a trava para permitir que o encadeamento em pausa continuasse.

  3. Ortogonal aos pontos anteriores, o uso de um Threadobjeto como bloqueio / mutex pode causar problemas. Por exemplo, o javadoc para Thread::joindiz:

    Esta implementação usa um loop de this.waitchamadas condicionadas this.isAlive. Quando um encadeamento termina, o this.notifyAllmétodo é chamado. Recomenda-se que os aplicativos não usar wait, notifyou notifyAllem Threadinstâncias.

Stephen C
fonte
2

Como você não publicou o código, estamos trabalhando no escuro. Quais são os detalhes da exceção?

Você está chamando Thread.wait () de dentro ou fora dele?

Eu pergunto isso porque, de acordo com o javadoc para IllegalMonitorStateException, é:

Lançado para indicar que um encadeamento tentou aguardar no monitor de um objeto ou para notificar outros encadeamentos que aguardam no monitor de um objeto sem possuir o monitor especificado.

Para esclarecer esta resposta, essa chamada para aguardar um thread também lança IllegalMonitorStateException, apesar de ser chamada de dentro de um bloco sincronizado:


     private static final class Lock { }
     private final Object lock = new Lock();

    @Test
    public void testRun() {
        ThreadWorker worker = new ThreadWorker();
        System.out.println ("Starting worker");
        worker.start();
        System.out.println ("Worker started - telling it to wait");
        try {
            synchronized (lock) {
                worker.wait();
            }
        } catch (InterruptedException e1) {
            String msg = "InterruptedException: [" + e1.getLocalizedMessage() + "]";
            System.out.println (msg);
            e1.printStackTrace();
            System.out.flush();
        }
        System.out.println ("Worker done waiting, we're now waiting for it by joining");
        try {
            worker.join();
        } catch (InterruptedException ex) { }

    }
CPerkins
fonte
@ CPerkins: Eu acho que você está confundindo o thread de execução e o objeto que é o alvo wait().
9789 Robert Munteanu
@ Robert - Talvez eu esteja, mas acho que não. Se você iniciar uma instância do Thread e pedir para aguardar, você receberá uma IllegalMonitorStateException, que é o que eu estava tentando descrever.
8138 CPerkins
Você está falando sobre a worker.wait()linha? Então você deve estar sincronizando no trabalhador, não no cadeado.
911 Robert Munteanu
1

Para lidar com o IllegalMonitorStateException, você deve verificar se todas as chamadas dos métodos wait, notify e notifyAll estão ocorrendo apenas quando o encadeamento de chamada possui o monitor apropriado . A solução mais simples é colocar essas chamadas dentro de blocos sincronizados. O objeto de sincronização que deve ser chamado na instrução sincronizada é aquele cujo monitor deve ser adquirido.

Aqui está o exemplo simples para entender o conceito de monitor

public class SimpleMonitorState {

    public static void main(String args[]) throws InterruptedException {

        SimpleMonitorState t = new SimpleMonitorState();
        SimpleRunnable m = new SimpleRunnable(t);
        Thread t1 = new Thread(m);
        t1.start();
        t.call();

    }

    public void call() throws InterruptedException {
        synchronized (this) {
            wait();
            System.out.println("Single by Threads ");
        }
    }

}

class SimpleRunnable implements Runnable {

    SimpleMonitorState t;

    SimpleRunnable(SimpleMonitorState t) {
        this.t = t;
    }

    @Override
    public void run() {

        try {
            // Sleep
            Thread.sleep(10000);
            synchronized (this.t) {
                this.t.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Rakesh Chaudhari
fonte
0

A chamada Thread.wait () faz sentido dentro de um código que sincroniza no objeto Thread.class. Eu não acho que é o que você quis dizer.
Você pergunta

Como posso fazer com que um thread aguarde até ser notificado?

Você pode fazer apenas seu thread atual aguardar. Qualquer outro segmento pode ser gentilmente solicitado a aguardar, se concordar.
Se você quiser esperar por alguma condição, precisará de um objeto de bloqueio - o objeto Thread.class é uma péssima escolha - é um AFAIK único, portanto a sincronização nele (exceto os métodos estáticos do Thread) é perigosa.
Detalhes para sincronização e espera já são explicados por Tom Hawtin. java.lang.IllegalMonitorStateExceptionsignifica que você está tentando esperar um objeto no qual não está sincronizado - é ilegal fazê-lo.

Tadeusz Kopec
fonte
0

Não tenho certeza se isso ajudará ou não alguém, mas esta foi a parte principal para corrigir meu problema na resposta do usuário "Tom Hawtin-tacklin" acima:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

Apenas o fato de o "bloqueio" ser passado como argumento em synchronized () e também ser usado em "bloqueio" .notifyAll ();

Uma vez que eu fiz nesses 2 lugares eu consegui trabalhar

jp093121
fonte
0

Recebi um IllegalMonitorStateExceptiontempo tentando ativar um thread em / de um class/ thread diferente . Em java 8você pode usar os lockrecursos da nova API de simultaneidade em vez de synchronizedfunções.

Eu já estava armazenando objetos para asynchronoustransações de websocket em um arquivo WeakHashMap. A solução no meu caso foi também armazenar um lockobjeto em umConcurrentHashMap para synchronousrespostas. Observe o condition.await(não .wait).

Para lidar com o multi threading, usei a Executors.newCachedThreadPool()para criar um pool de threads .

Stuart Cardall
fonte
0

Aqueles que estão usando o Java 7.0 ou versão inferior podem consultar o código que eu usei aqui e ele funciona.

public class WaitTest {

    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void waitHere(long waitTime) {
        System.out.println("wait started...");
        lock.lock();
        try {
            condition.await(waitTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
        System.out.println("wait ends here...");
    }

    public static void main(String[] args) {
        //Your Code
        new WaitTest().waitHere(10);
        //Your Code
    }

}
homem
fonte