Exemplo de Java Thread wait, notify e notifyAll

A classe Object em Java contém três métodos finais que permitem que as threads comuniquem sobre o status de bloqueio de um recurso. Esses métodos são wait(), notify() e notifyAll(). Então hoje vamos examinar wait, notify e notifyAll em um programa Java.

wait, notify e notifyAll em Java

A thread atual que invoca esses métodos em qualquer objeto deve ter o monitor do objeto, caso contrário, ele lança uma exceção java.lang.IllegalMonitorStateException.

wait

Os métodos de espera do objeto têm três variações, uma que espera indefinidamente para que qualquer outra thread chame o método notify ou notifyAll no objeto para acordar a thread atual. As outras duas variações colocam a thread atual em espera por um período específico antes de acordarem.

notify

O método notify acorda apenas uma thread que está à espera no objeto, e essa thread inicia a execução. Portanto, se houver várias threads à espera de um objeto, este método acordará apenas uma delas. A escolha da thread a acordar depende da implementação do sistema operacional na gestão de threads.

notifyAll

O método notifyAll acorda todas as threads à espera no objeto, embora a ordem de processamento dependa da implementação do sistema operacional. Esses métodos podem ser usados para implementar o problema do produtor-consumidor, onde as threads consumidoras aguardam pelos objetos na fila, e as threads produtoras colocam objetos na fila e notificam as threads em espera. Vamos ver um exemplo em que várias threads trabalham no mesmo objeto, e utilizamos os métodos wait, notify e notifyAll.

Mensagem

A java bean class on which threads will work and call wait and notify methods.

package com.journaldev.concurrency;

public class Message {
    private String msg;
    
    public Message(String str){
        this.msg=str;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String str) {
        this.msg=str;
    }

}

Espera

A class that will wait for other threads to invoke notify methods to complete it’s processing. Notice that Waiter thread is owning monitor on Message object using synchronized block.

package com.journaldev.concurrency;

public class Waiter implements Runnable{
    
    private Message msg;
    
    public Waiter(Message m){
        this.msg=m;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        synchronized (msg) {
            try{
                System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());
                msg.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());
            //processar a mensagem agora
            System.out.println(name+" processed: "+msg.getMsg());
        }
    }

}

Notifier

A class that will process on Message object and then invoke notify method to wake up threads waiting for Message object. Notice that synchronized block is used to own the monitor of Message object.

package com.journaldev.concurrency;

public class Notifier implements Runnable {

    private Message msg;
    
    public Notifier(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name+" started");
        try {
            Thread.sleep(1000);
            synchronized (msg) {
                msg.setMsg(name+" Notifier work done");
                msg.notify();
                // msg.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }

}

WaitNotifyTeste

Classe de teste que criará múltiplas threads de Waiter e Notifier e as iniciará.

package com.journaldev.concurrency;

public class WaitNotifyTest {

    public static void main(String[] args) {
        Message msg = new Message("process it");
        Waiter waiter = new Waiter(msg);
        new Thread(waiter,"waiter").start();
        
        Waiter waiter1 = new Waiter(msg);
        new Thread(waiter1, "waiter1").start();
        
        Notifier notifier = new Notifier(msg);
        new Thread(notifier, "notifier").start();
        System.out.println("All the threads are started");
    }

}

Ao invocarmos o programa acima, veremos a saída abaixo, mas o programa não será concluído porque há duas threads esperando no objeto Message e o método notify() só acorda uma delas, enquanto a outra thread ainda está esperando para ser notificada.

waiter waiting to get notified at time:1356318734009
waiter1 waiting to get notified at time:1356318734010
All the threads are started
notifier started
waiter waiter thread got notified at time:1356318735011
waiter processed: notifier Notifier work done

Se comentarmos a chamada notify() e descomentarmos a chamada notifyAll() na classe Notifier, a saída produzida será a seguinte:

waiter waiting to get notified at time:1356318917118
waiter1 waiting to get notified at time:1356318917118
All the threads are started
notifier started
waiter1 waiter thread got notified at time:1356318918120
waiter1 processed: notifier Notifier work done
waiter waiter thread got notified at time:1356318918120
waiter processed: notifier Notifier work done

Já que o método notifyAll() acorda ambas as threads Waiter e o programa completa e termina após a execução. Isso é tudo para wait, notify e notifyAll em Java.

Source:
https://www.digitalocean.com/community/tutorials/java-thread-wait-notify-and-notifyall-example