Thread - wait() / notify()

Galera,

Tenho o seguinte cenário:

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

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”??

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…

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!

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!!

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.

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

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:

[code]public class ThreadA {

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

}

[/code]

  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. :wink:

[quote=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:

[code]public class ThreadA {

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

}

[/code]

  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. :wink:
[/quote]

Olá ViniGodoy

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

[quote=CorreaThiago]Olá ViniGodoy

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