Looping (wait and notify)

Exemplo 1:

Ambas as threads (operador e máquina) são iniciadas e ficam em looping infinito. A thread da máquina tão logo que é iniciada, se não contiver nenhum trabalho a realizar, então entra em estado de espera (método wait).

Podemos observar que no método main da classe inicializadora (TestThread14) são inseridos novos trabalhos (job) para a máquina (class Machine) através do operador (class Operator).

Se, a fila de trabalhos do operador (jobsToDo) não estiver vazia, a thread que está executando o operador (em seu método run), executará o método ‘machine.addJob(jobsToDo.remove(0));’ no objeto da classe Machine que o operador tem a referência. O método ‘Machine.addJob’ por sua vez, sendo executado pela thread do Operador, notifica a thread da máquina em sair do estado de espera (wait) através da instrução ‘jobs.notify();’ acarretando na execução do método run da classe Machine. (para melhorar o entendimento, veja o output abaixo e repare as linhas cujas threads estão na cor roxa).

Falando em português claro como este código funciona: sempre que o operador adicionar um novo trabalho em sua fila, ele passará para a máquina este trabalho e a notificará de que ela pode sair do estado de espera (wait) e executar o mesmo.

import java.util.List;

import java.util.ArrayList;

public class TestThread14 {

public static void main(String[] args) {

Machine machine = new Machine();

Operator operator = new Operator(machine);

// operador inserindo novas ordens para máquina

operator.queueJob(new MachineInstructions(1, 1));

operator.queueJob(new MachineInstructions(2, 2));

Thread tmaquina = new Thread(machine, “Thread-Machine”);

Thread toperador = new Thread(operator, “Thread-Operator”);

// a thread da máquina será iniciada e entrará em estado de espera

// até que seja inserido um trabalho nela.

tmaquina .start();

toperador.start();

// operador inserindo novas ordens para máquina

operator.queueJob(new MachineInstructions(3, 3));

operator.queueJob(new MachineInstructions(4, 4));

}

}

class Operator extends Thread {

Machine machine;

List<MachineInstructions> jobsToDo= new ArrayList<MachineInstructions>();

public Operator(Machine machine) {

this.machine = machine;

}

public void run() {

while (true) {

// se o operador tiver trabalhos na fila dele então ele insere na maquina.

if (!jobsToDo.isEmpty()) {

machine.addJob(jobsToDo.remove(0));

}

}

}

public void queueJob(MachineInstructions job) {

System.out.println(“\tOPERATOR: queueJob ” + job);

this.jobsToDo.add(job);

}

}

class Machine implements Runnable {

List<MachineInstructions> jobs = new ArrayList<MachineInstructions>();

public void addJob(MachineInstructions job) {

synchronized (jobs) {

jobs.add(job);

System.out.println(“MACHINE: *** starting to do job: ” + job);

jobs.notify();

}

}

@Override

public void run() {

while (true) {

System.out.println(“MACHINE: looking for work to do !!!”);

synchronized (jobs) {

// wait until at least one job is available

while (jobs.isEmpty()) {

/*

If there are no jobs, it will start waiting. If it’s notified, it will stop waiting and then recheck the loop condition: is the list still empty? In practice this doublecheck is probably not necessary, as the only time a notify() is ever sent is when a new job has been added to the list. However, it’s a good idea to require the thread to recheck the isEmpty() condition whenever it’s been woken up, because it’s possible that a thread has accidentally sent an extra notify() that was not intended.

By putting the wait() method in a while loop and re-checking the condition that represents what we were waiting for, we ensure that whatever the reason we woke up, we will re-enter the wait() if (and only if) the thing we were waiting for has not happened yet.

*/

try {

System.out.println(“MACHINE: no works to do. WAITING ….”);

jobs.wait();

} catch (InterruptedException ie) {

}

}

// If we get here, we know that jobs is not empty

MachineInstructions instructions = jobs.remove(0);

// Send machine steps to hardware

System.out.println(“MACHINE: send work to hardware: ” + instructions);

}

}

}

}

// classe apenas de apoio ao código…

class MachineInstructions {

public MachineInstructions(int depth, int width) {

this.depth = depth;

this.width = width;

}

int depth = 0;

int width = 0;

@Override

public String toString() {

return “depth: ” + depth + ” width ” + width;

}

}

Output:

Thread executando Descrição
main OPERATOR: queueJob depth: 1 width 1 Operador insere primeira operação
main OPERATOR: queueJob depth: 2 width 2 Operador insere segunda operação
Thread-Machine MACHINE: looking for work to do !!! Maquina é ativada
Thread-Machine MACHINE: no works to do. WAITING …. Maquina ainda não percebe seu trabalho a fazer então entra em estado de espera….
main OPERATOR: queueJob depth: 3 width 3 Operador insere terceira operação
main OPERATOR: queueJob depth: 4 width 4 Operador insere quarta operação
Thread-Operator MACHINE: *** starting to do job: depth: 1 width 1 Maquina será notificada a sair do estado de espera pela Thread-Operator
Thread-Operator MACHINE: *** starting to do job: depth: 2 width 2
Thread-Operator MACHINE: *** starting to do job: depth: 3 width 3
Thread-Operator MACHINE: *** starting to do job: depth: 4 width 4
Thread-Machine MACHINE: send work to hardware: depth: 1 width 1 Maquina envia primeiro trabalho para o hardware
Thread-Machine MACHINE: looking for work to do !!!
Thread-Machine MACHINE: send work to hardware: depth: 2 width 2 Maquina envia segundo trabalho para o hardware
Thread-Machine MACHINE: looking for work to do !!!
Thread-Machine MACHINE: send work to hardware: depth: 3 width 3 Maquina envia terceiro trabalho para o hardware
Thread-Machine MACHINE: looking for work to do !!!
Thread-Machine MACHINE: send work to hardware: depth: 4 width 4 Maquina envia quarto trabalho para o hardware
Thread-Machine MACHINE: looking for work to do !!!
Thread-Machine MACHINE: no works to do. WAITING …. Thread da maquina entra em estado de espera pois acabaram os trabalhos a serem feitos.

(Neste momento, após imprimir todas as intruções e para esta execução o programa continuará eternamente em memória. Enquanto a thread Machine fica em estado de espera eterno, a thread do Operador fica em looping infinito esperando existir um novo trabalho na jobsToDo para passar novamente para a thread Machine.)

Exemplo 2:

A importância deste segundo exemplo, é esclarecer qual thread que está executando o método wait() e o método notify().

public class TestThread15 {

public static void main(String[] args) {

CountMachine c = new CountMachine();

Thread t = new Thread(c);

t.start();

c.stop();

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

c.start();

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

c.stop();

}

}

class CountMachine implements Runnable {

private Boolean start = true;

private long i = 0;

@Override

public void run() {

while (true) {

/*

neste caso, a condição para que esta thread continue em estado de espera

é o fato do campo ‘start’ desta classe permanecer em estado falso como

pode-se ver abaixo

*/

while (!start) {

try {

System.out.println(Thread.currentThread().getName()+ ” PAROU !!!”);

this.i = 0;

synchronized (this) {

this.wait();

}

} catch (InterruptedException ex) {}

}

i++;

System.out.println(Thread.currentThread().getName() + ” – ” + this);

try {

Thread.sleep(1000);

} catch (InterruptedException ex) {

}

}

}

public void start() {

/*

Se modificarmos o estado do campo ‘start’para verdadeiro, então devemos

notificar a thread de que o estado do campo ‘start’ mudou. Pode-se reparar

que no método ‘run()’ acima, será redundantemente verificado se o estado

do campo ‘start’ é verdadeiro ou falso. Caso seja falso, a thread novamente

entrará em estado de espera.

*/

this.start = true;

synchronized (this) {

System.out.println(“Thread ” + Thread.currentThread().getName() + ” método START() invoked”);

this.notify();

}

}

public void stop() {

System.out.println(“Thread ” + Thread.currentThread().getName() + ” método STOP() invoked”);

this.start = false;

}

@Override

public String toString() {

return “Machine value = ” + i;

}

}

Output:

Thread main método STOP() invoked Veja que quem chama o método stop é a thread main
Thread-0 – Machine value = 1
Thread-0 PAROU !!!
Thread main método START() invoked Veja que quem chama o método start é a thread main
Thread-0 – Machine value = 1
Thread-0 – Machine value = 2
Thread-0 – Machine value = 3
Thread main método STOP() invoked Veja novamente que quem chama o método stop é a thread main
Thread-0 – Machine value = 4
Thread-0 PAROU !!!

(Para esta execução deste program ‘TestThread15.main()’, o programa continua em memória pois a Thread-0 fica em eterno estado de espera)

Anúncios
Esta entrada foi publicada em Threads com as etiquetas . ligação permanente.

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s