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.
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??
É 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()??
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:
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;
É 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.
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.