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

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

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

}[/code]

Classe Consumidor

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

}[/code]

Classe Pilha

[code]
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;
}
}[/code]

Classe Start

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

}[/code]