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 se comuniquem sobre o status do bloqueio de um recurso. Esses métodos são wait(), notify() e notifyAll(). Portanto, hoje vamos analisar os métodos 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, lançará 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 uma quantidade específica de tempo antes de acordarem.

notify

notify método acorda apenas uma thread em espera no objeto e essa thread inicia a execução. Portanto, se houver várias threads aguardando por um objeto, este método acordará apenas uma delas. A escolha da thread a ser acordada depende da implementação do sistema operacional no gerenciamento de threads.

notifyAll

O método notifyAll acorda todas as threads em espera no objeto, embora a ordem de processamento dependa da implementação do sistema operacional. Esses métodos podem ser utilizados 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 onde 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;
    }

}

Atendente

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());
        }
    }

}

Notificador

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();
        }
        
    }

}

TesteWaitNotify

Classe de teste que criará vários threads de Waiter e Notifier e os 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 chamar o programa acima, veremos a seguinte saída, mas o programa não será concluído porque há dois threads aguardando no objeto de mensagem e o método notify() acordará apenas um deles; o outro thread ainda está esperando ser notificado.

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 Notificador, 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

Visto que o método notifyAll() acorda ambos os threads Waiter, o programa é concluído e encerrado 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