Questão de Thread_Livro Kathy pag 411

Ola a todos, alguém poderia me explicar este código:

public class ThreadA {
	
	public static void main(String[] args) {
		ThreadB b= new ThreadB();
		b.start();
		
		
		synchronized (b) {
			try {
				System.out.println("Waiting for b to complete...");
				b.wait();
			} catch (InterruptedException e) {}
			System.out.println("Total is " + b.total);
		}
		
	}	
}


public class ThreadB extends Thread {
	int total;
	
	@Override
	public void run(){
		synchronized (this) {
			for (int i = 0; i < 100; i++) {
				total+=i;
			}
			this.notify();
		}
		
	}

}

Quando a ThreadMain representada pela classe ThreadA, cheguar na linha 5 b.start();, ela chamara a ThreadB, que começará a executar o seu metodo Run(), que é synchronized, até ai tudo bem. Mas quando a threadA continuar executando as outras linhas de sua classe chegará no metodo:

synchronized (b) {
			try {
				System.out.println("Waiting for b to complete...");
				b.wait();
			} catch (InterruptedException e) {}
			System.out.println("Total is " + b.total);
		}

Só que ao meu modo de ver esta instancia “b”, já estará bloqueada pela ThreadB, que bloquea a instancia atual this, que neste caso é o objeto “b”, então esta threadA, não poderia obter o bloqueo deste objeto, não podendo assim chamar wait(), gerando uma excessão IllegalMonitorStateException. Mas acontece que essa exceção não é gerada eh o programa funciona perfeitamente.

Alguem poderia me exlicar porque??

Obrigado

Primeiramente, esse programa está muito mal implementado. São exemplos como esse que levam a um código instável. Organização, sobretudo num sistema multi-threading é tudo.

Mas, ok, vamos a dúvida.

Esse programa parte do pressuposto perigoso de que a Thread A entrará no trecho sincronizado antes da thread B começar a executar. O que nem sempre é verdade.

Agora, a situação do IllegalMonitorStateException que vc descreveu nunca ocorre. Suponha que B processe primeiro e entre no trecho synchronized(this).

Se nesse momento a Thread A processar, ela irá dormir assim que encontrar a linha “synchronized (b)”, e só será despertada quando B sair de seu próprio trecho sincronizado. A execução de B então continua, B deixa o trecho sincronizado e dá o notify(). A thread A desperta (não por causa do notify(), mas pq B saiu da área sincronizada), vai até a linha do wait() e dorme para sempre (já que B já deu seu notify()).

No fluxo normal, esperado pela autora do livro, a thread A entra primeiro no bloco sincronizado, chama o wait, dorme e espera por B.

Esse é um bom exemplo de como não se lidar com Threads.

Obrigado Viny pela resposta, só me esclarece mais uma dúvida então quando eu chamo por exemplo:

public class Teste(){
ThreadB b= new ThreadB();  
  b.wait();

}

Aqui na verdade o b.wai() irá pegar a thread atual, que é main() e colocar na fila de espera de “b” até que “b” dê o seu notify, ai sim ela irá acordar, seria isso?

Outra coisa entao o IlegalMonitorStateException só ocorreria se eu chamasse wait() fora de um metodo synchronized, porque se o bloqueo já estivesse com outra thread a atual esperaria até entrar no metodo para chamar o wait() , certo??

Obrigado

É isso mesmo. Mas esse trecho teria que estar dentro de um synchronized(b), ou então, vai lançar IllegalMonitorStateException.

[quote=danielbussade]
Outra coisa entao o IlegalMonitorStateException só ocorreria se eu chamasse wait() fora de um metodo synchronized, porque se o bloqueo já estivesse com outra thread a atual esperaria até entrar no metodo para chamar o wait() , certo??[/quote]

É isso mesmo. Essa exceção só ocorre quando vc não tem o controle do monitor. E como se obtém um monitor?

synchronized(objetoMonitor) { //Aqui temos o monitor }

Acontece que o monitor só é liberado se nenhuma outra thread o possuir. Você não estudou semáforos em Sistemas Operacionais ou Sistemas Distribuídos? Monitores são uma simplificação do mecanismo de semáforos, originalmente criado pelo Dijkstra. São equivalentes a semáforos com um contador que só vai até 1.

Viny muito obrigado pelas respostas; Kra, a faculdade que eu fiz é muito fraca, tudo que sei aprendi por conta propria estudando, inclusive nem a materia SO tinha na grade, então sou ouvi falar sobre semáforo nunca estudei sobre os mesmos, mas vou procurar um material pra entender melhor, pois o assunto eh bem interessante.

Agora olha só ,neste caso do primeiro programa, a intenção dela era só deixa A executar depois que o B terminasse, então não seria melhor ela usar b.join()??

Valeu

Abraço

Aliás, não sei pq a Kathy deixa o código tão confuso. Talvez para treinar a sua atenção. Mas há uma série de coisas nesse livro que são péssimas práticas de programação:

  1. Geralmente, ao usar synchronized ou você usa sobre o this, ou você cria um objeto especificamente para servir de monitor. Geralmente é um objeto simples como um int[0]. Agora, jamais misture as duas coisas, como ela fez no caso de this e b. Isso só leva a código confuso;

  2. É preferível implementar Runnable do que estender thread. Isso porque usualmente se separa o mecanismo que dispara a thread, do código que será executado pela thread. Lembre-se a classe Thread em si não é a thread. Ela só é uma forma de criar uma thread e obter informações sobre a thread criada.

  3. Nunca se assume nada a respeito do escalonador, como esse programa fez. Isso gera a um código que poderá falhar sem aviso prévio e, pela lei de Murphy, na mão do seu cliente. Esse é um princípio que deve ser estressado desde os primeiros exemplos.

Sim, seria. Bem melhor. Mas acho que ela queria mostrar o uso de wait() e notify().