Exemplo de Thread Produtor/Consumidor utilzando wait() e notify()

0 respostas
B

Blz pessoal,
recentemente estava com algumas duvidas na utilização dos métodos wait() e notify() para a manipulação de Thread's juntamente com o modificador synchronized, então resolvi estudar um pouco e criar o famoso programa Produtor / Consumidor, é claro utilizando o recurso de Thread's.

Esclarecimentos:
Há um tempo atrás eu imaginava que poderia executar duas Threads (Thead A e Thread B), e dizer assim: "A agora você para (utilizando wait()) e B comece a trabalhar (utilizando notify()) fora de um contexto sincronizado", tão logo descobri que, para que os métodos wait() e notify() funcionem eles devem sempre estar dentro de um bloco synchronized, no inicio tive muitos problemas de mandar uma Thread parar (wait()), o programa ficava eternamente em espera(famoso dead lock) ou até mesmo lançava exceptions ILegalMonitorExceptions.

Fiz algumas loucuras do tipo, passar para o construtor da Thread A a Thread B e vice-versa e aplicar wait e notify na esperança de parar a Thead e em seguida coloca-la em estado de execução, resultado: consegui construir o caos, enumeras exceptions e travamento de maquina.

Em fim, é com os erros que aprendemos mas procure não errar tanto. hahahah. Como recomendação executem esse código normalmente e em seguinda adicione alguns breakpoints para uma melhor compreensão com o modo debug.

Vou postar o código aqui para quem tiver duvidas na utilização do wait() bem como notify(), tenham um exemplo para se embasar em codificações utilizando Thread's.

Creio que após o estudo desse código, a construção de Threads tornará-se mais amigável.

Considerações

Observe que nas classes Produtor e Consumidor onde há o comentário (número um) contem a chamada do método wait() logo na inicialização, isso foi necessário pois devemos considerar que, de uma pilha com tamanho finito, não podemos retirar elementos caso a mesmo esteja vazia, em contra partida não podemos adicionar elementos se a mesma já se encontra totalmente cheia.
Desse modo, quando o loop da classe Start é inciado existe uma checagem para verificar se a pilha está fazia (número dois), caso esteja o Produtor é notificado e começa a produção, ao termino da produção, a execução vai para a próxima linha (número três). Então é feita novamente uma checagem para verificar se a pilha está cheia, sendo verdadeiro o Consumidor é notificado e começa a retirada da pilha, sendo assim os próximos processo são executados sucintamente.

Obs
Tal código foi criado para expressar a codificação de Threads com a utilização de seus métodos de controle. Vale ressaltar que as classes abaixo são meramente ilustrativas sendo que para criar
um verdadeiro Produtor / Consumidor é necessário mais controle e uma melhor elaboração para a codificação do problema.

Classe Produtor
package br.com.exe;

public class Produtor implements Runnable {
	Pilha pilha;
	int bufferSize;

	Produtor(Pilha pilha, int bufferSize) {
		this.pilha = pilha;
		this.bufferSize = bufferSize;
	}

	public void run() {
		while (true) {
			synchronized (this) {
				try {
					wait();  // -----> 1
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				while (pilha.get() >= 0 && pilha.get() <= (bufferSize-1)) {
					pilha.put(pilha.get() + 1);
					System.out.println("Produzindo -> " + pilha.get());
				}
			}
		}
	}

	public void init() {
		new Thread(this, "Produtor").start();
	}
}

Classe Consumidor

package br.com.exe;

public class Consumidor implements Runnable {
	Pilha pilha;

	public Consumidor(Pilha pilha) {
		this.pilha = pilha;
	}

	public void run() {
		while (true) {
			synchronized (this) {
				try {
					wait();  // ---------------- 1
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				while (pilha.get() != 0) {
					System.out.println("Consumindo -> " + pilha.get());
					pilha.put(pilha.get() - 1);
				}
			}
		}

	}

	public void init() {
		new Thread(this, "Consumidor").start();
	}

}
Classe Pilha
package br.com.exe;

public class Pilha {
	int n = 0;
	synchronized int get(){
		return n;
	}
	synchronized int put(int n){
		this.n = n;
		return this.n;
	}
}
Classe Start
package br.com.exe;

public class Start {

	public static void main(String[] args) {

		new Runnable() {
			@Override
			public void run() {
				final int bufferSize = 10;

				Pilha pilha = new Pilha();
				Produtor p = new Produtor(pilha, bufferSize);
				Consumidor c = new Consumidor(pilha);

				p.init();
				c.init();

				while (true) {
					if (pilha.get() == 0) { // -----------> 2
						synchronized (p) {
							System.out.println("************************* Incializa Produção *************************");
							p.notify();
						}
					}

					if (pilha.get() == bufferSize) { // -------------> 3
						synchronized (c) {
							System.out.println("************************* Incializa Consumo *************************");
							c.notify();
						}
					}
				}

			}
		}.run();
	}
}
Criado 26 de novembro de 2009
Respostas 0
Participantes 1