Thread - wait() / notify()

8 respostas
P

Galera,

Tenho o seguinte cenário:

class ThreadA{ 
       
       public static void main(String[] args) {
             
             ThreadB threadB = new ThreadB();
             theadB.start();

             try{
	   synchronized (threadB) {  //captuta o bloqueio da instância 
	      System.out.println("Waiting for threadB to complete...");
	
                      theadB.wait(); // Thread A aguarda o Thread B
	   }
              }catch(Exception exception){}
	
                   System.out.println(threadB.total);  // valor que será exibido 
                                                                      
        }
}

class ThreadB extends Thread{
	long total;
	public void run(){
		synchronized (this){
			for(long i = 0; i <1000000000l; i++){
				total =+ i;
			}
			notify(); // notifica o ThreadA qdo. o 
		                             // for tiver terminado   
                                 }               
	}
}

A dúvida é:

Mesmo quando ñ chamo o método notify em ThreadB o ThreadA exibe o valor completo de total!!

Se notify ñ fosse chamado o ThreadA ñ deveria aguardar "eternamente"??

8 Respostas

ViniGodoy

Se você não colocar o wait dentro de um while, a thread A poderá acordar espontaneamente. Se você ler o Javadoc do wait, verá que threads estão sugeitas a spurious wakeups. Ou seja, acordar sem razão nenhuma.

Até onde tenho observado, a morte de uma thread é uma das razões pelo qual um wakeup desses ocorre…

LPJava

assim… so completando o que o vinny falou… que é um expert em thread heeh bom… a kathy fala o seguinte: " nao é pq uma thread está dormindo q ela vai ficar para sempre se o agendador decidir acordar, ela acorda e executa se ele desejar" Entao quando se trata de threads nao é garantido… tudo depende do agendador… tempo de execucao e talz… e o java doc recomenda o uso do while acontece o que o vinny falou…
Mas lembre-se therads não garantia… nos programadores… podemos usar metodos que influencia na sua execucao…

P.S: se eu tiver equivocado so corrigir ai…

flw!

P

Tô entendendo +/-.

Então pq o ThreadA exibe o valor de total só qdo termina o loop em Thread B??

Mas, os métodos wait e notify ñ devem ser usados juntos?!!

Então p/ q notify??

A propósito LPJava onde está escrito:

no livro da Kathy??

Falow!!

ViniGodoy

O notify força que uma das threads que está esperando pelo lock acorde imediatamente.
Sem ele, você não tem como prever quando o scheduler do seu SO vai resolver acordar a thread.

Mais interessante do que usar o notify é usar o notifyAll(). Ele vai avisar todas as threads que estiverem dormindo.

Um problema do notify é que se a thread acordada não tiver condições de entrar no trecho sincronizado, ela volta a dormir. Isso é mesmo correto, mas outras threads que teriam condições não acordarão e não entrarão no código sincronizado.

Com o notifyAll, todas as threads acordam. Aquela que não tem condições de entrar volta a dormir, mas a que tem, entra no trecho sincronizado. Isso evita deadlocks no seu sistema, mesmo que seja um pouco mais caro em termo de recursos. Garalmente não é um problema, afinal, se o gargalo do seu sistema estiver no notify, você tem um problema sério.

deyvid

Só uma dúvida, o que acontece com ThreadA quando ela rebebe o wait? E se outra thread tentar acessar ThreadA vai poder executar algum método dela??

Falow

ViniGodoy

Primeiro de tudo, vamos eliminar uma confusão comum, que aparentemente você está cometendo.

Existem 2 coisas distintas:

a) A classe Thread, e seus objetos. Nesse caso, representados pela ThreadA. Elas descrevem as threads em execução e disparam novas threads.
b) As threads em execução propriamente ditas. Elas representam os diversos fluxos do programa ou a linha que vc vê avançando sobre o programa, quando usa seu debugger.

Dito isso, que fique claro que a sincronização atua sobre threads e não sobre os objetos da classe Thread. Ou seja, ThreadA, quando recebe um wait(), não faz nada. O objeto não muda de estado, a menos que a thread que tenha rodado esse wait, seja o mesmo que está o objeto da classe ThreadA está representando.

Quem muda de estado é a thread em si, que pode ter sido disparada em qualquer classe que tenha estendido Runnable e sido iniciada com o start() da classe Thread.

Então, veja os passos que todas as threads fazem num código como esse:

public class ThreadA {

     public void metodoA() {
           synchronized(this) {
                 wait();
           }
     }
     //mais coisa aqui pra baixo
}

1. Uma thread entra no metodoA();
2. Ela chega no trecho sincronizado. Então, verifica se o monitor (this) está disponível;
3. Se estiver, ela reserva o monitor para si, e entra no trecho sincronizado;
4. Ela encontra o wait(). Põe-se a dormir, e libera o monitor.

E se o monitor não estiver disponível no passo 2? Então:
3 (alternativo). A thread dorme, entrando numa fila até que o monitor esteja disponível;

Quando ocorre um notify, o que acontece?

A primeira thread da fila que está dentro do wait() desperta. Se ela não puder despertar, nada acontece (mesmo que existam outras threads nesse wait, que poderiam despertar, nada acontece).

E quando ocorre um notifyAll()?
A todas as threads da fila que estão dentro do wait() despertam, entretanto, só uma pegará o monitor da área sincronizada de cada vez. Se qualquer uma delas puder despertar, o monitor irá para a primeira que despertar e as demais voltam a dormir.

E o que acontece quando a thread que está na região sincronizada, sai dessa região?
Se houverem threads esperando na entrada do sincronized (this) elas acordam, repetindo o comportamento "equivalente" ao do notifyAll().

Note que estamos falando de threads. Pouco importa em que classe a thread tenha sido originalmente disparada, ou em que classe a thread está. Pense sempre nas linhas de execução, não no objeto. O único objeto que no final das contas importa é o que está sendo usado para a sincronização.

Espero que a dúvida tenha sido resolvida. Qualquer coisa, é só perguntar. ;)

C
ViniGodoy:
Primeiro de tudo, vamos eliminar uma confusão comum, que aparentemente você está cometendo.

Existem 2 coisas distintas:

a) A classe Thread, e seus objetos. Nesse caso, representados pela ThreadA. Elas descrevem as threads em execução e disparam novas threads.
b) As threads em execução propriamente ditas. Elas representam os diversos fluxos do programa ou a linha que vc vê avançando sobre o programa, quando usa seu debugger.

Dito isso, que fique claro que a sincronização atua sobre threads e não sobre os objetos da classe Thread. Ou seja, ThreadA, quando recebe um wait(), não faz nada. O objeto não muda de estado, a menos que a thread que tenha rodado esse wait, seja o mesmo que está o objeto da classe ThreadA está representando.

Quem muda de estado é a thread em si, que pode ter sido disparada em qualquer classe que tenha estendido Runnable e sido iniciada com o start() da classe Thread.

Então, veja os passos que todas as threads fazem num código como esse:

public class ThreadA {

     public void metodoA() {
           synchronized(this) {
                 wait();
           }
     }
     //mais coisa aqui pra baixo
}

1. Uma thread entra no metodoA();
2. Ela chega no trecho sincronizado. Então, verifica se o monitor (this) está disponível;
3. Se estiver, ela reserva o monitor para si, e entra no trecho sincronizado;
4. Ela encontra o wait(). Põe-se a dormir, e libera o monitor.

E se o monitor não estiver disponível no passo 2? Então:
3 (alternativo). A thread dorme, entrando numa fila até que o monitor esteja disponível;

Quando ocorre um notify, o que acontece?

A primeira thread da fila que está dentro do wait() desperta. Se ela não puder despertar, nada acontece (mesmo que existam outras threads nesse wait, que poderiam despertar, nada acontece).

E quando ocorre um notifyAll()?
A todas as threads da fila que estão dentro do wait() despertam, entretanto, só uma pegará o monitor da área sincronizada de cada vez. Se qualquer uma delas puder despertar, o monitor irá para a primeira que despertar e as demais voltam a dormir.

E o que acontece quando a thread que está na região sincronizada, sai dessa região?
Se houverem threads esperando na entrada do sincronized (this) elas acordam, repetindo o comportamento "equivalente" ao do notifyAll().

Note que estamos falando de threads. Pouco importa em que classe a thread tenha sido originalmente disparada, ou em que classe a thread está. Pense sempre nas linhas de execução, não no objeto. O único objeto que no final das contas importa é o que está sendo usado para a sincronização.

Espero que a dúvida tenha sido resolvida. Qualquer coisa, é só perguntar. ;)

Olá ViniGodoy

Consegui entender seu exemplo, mas poderia disponibilizar um exemplo só para que eu possa começar?
Obrigado! t++;

ViniGodoy

CorreaThiago:
Olá ViniGodoy

Consegui entender seu exemplo, mas poderia disponibilizar um exemplo só para que eu possa começar?
Obrigado! t++;

Criado 10 de novembro de 2007
Ultima resposta 13 de set. de 2014
Respostas 8
Participantes 5