Wait() e notify() duvida

ae pessoal lendo o finalzinho do livro da kathy cap 9. fiquei em uma duvida desses metodo wait() e notify(). olha a sintaxe abaixo:

class ThreadA{
public static void main(String [] args){
	ThreadB b = new ThreadB();
	b.start();
	//sicronizo o objeto e o bloco do codigo
	synchronized(b){
		try{
			System.out.println("waiting for b complete...");
			b.wait();
		}catch(InterruptedException e){}
		System.out.println("Total " + b.total);
	}
}
}

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

queri entender uma coisa… quando uso o wait() eu digo para Thread que sincronizei ei espere ai… ate vc receber uma notificação para continuar… seria isso?
entao minha thread na linha 9 para a execução da threadA e essa thread so continua quando a threadB b receber notify()? eu fiquei meio confuso na implementacao desses metodos… sao bastante legais mais… assim uma thread sempre fica esperando la? sem fazer nada ate que receba uma notificação?

e outro codigo que a kathy disse que se eu alterar a linha 36 para notify() apenas um thread será notificado mudei e nao vi efeito no resultado… :?

class Reader extends Thread{
	Calculator c;
	
	public Reader(Calculator calc){
		c = calc;
	}
	
	public void run(){
		synchronized(c){
			try{
				System.out.println("waiting for calculation..");
				c.wait();
			}catch(InterruptedException e){}
		}
		System.out.println("Total is " + c.total);
	}
	
	public static void main(String args[]){
		//objeto da thread
		Calculator calculator = new Calculator();
		
		new Reader(calculator).start();
		new Reader(calculator).start();
		new Reader(calculator).start();
		calculator.start();
	}
}
class Calculator extends Thread{
	int total;
	public void run(){
		synchronized(this){
			for(int i=0;i<100;i++){
				total+=1;
			}
			//libera toda as tres thread em espera de Readrs
			notifyAll();
		}
	}
}

maior duvida nesse finalizinho… quem puder dar esse help ai agradeço!!

Oi Camilo.

Lembra da chave que comentei para você no outro post? Então, o comando notify()/notifyAll() serve para acordar threads que estão dormindo sob o controle daquela chave.

Ou seja, se você tiver a chave “chave” o comando:

synchronized (chave) { wait(); }
Será acordado por:

synchronized (chave) { notifyAll(); }
Agora, um detalhe. O Java está sujeito a spurious wakeup, ou seja, de vez enquando o wait() pode resolver acordar sem motivo algum. Você pode deixar o seu código mais claro e evitar esse problema associando uma condição ao wait(), através de um while:

synchronized (chave) { while (condicaoDeEspera) wait(); }

Agora vamos dar uma olhada no seu exemplo. O finalzinho usa this, na classe Calculator, para que a sincronização ocorra em cima da própria variável que for criada do tipo calculator. Ou seja, um notifyAll() por ali, acordará todos os que estiverem usando o calculator para um wait(). É o caso das classes Reader.

Entretanto, certamente essa não é a maneira mais usual de se fazer isso. Primeiro que gerou um código confuso. Simplesmente temos a impressão que this da classe calculator e c da classe reader não se referem ao mesmo objeto.

Em segundo lugar, normalmente teremos classes distintas e a forma que elas controlam seu sincronismo fica encapsulado. Ou seja, você não poderia assumir que a classe Calculator sincroniza a si mesma e, portanto, não seria uma boa idéia fazer o wait() do Reader dessa forma.

Portanto, existem duas maneiras mais usuais de se fazer isso:

  1. Você define uma relação onde a calculadora notifica os readers. Isso subaproveita um pouco o mecanismo do wait, mas gera um código muito mais claro e mais reutilizável;

  2. Você utiliza um objeto de sincronização externo e faz tudo dentro de uma classe mediadora. Isso mantém forte dependência entre as classes, portanto essa técnica geralmente é usada em inner classes privadas e é mais próxima do que você quis fazer.

Deixei um exemplo de cada implementação abaixo.

Ok, abaixo vai um código mais usual.

import java.util.HashSet;
import java.util.Set;

[code]/**

  • Temos uma classe reader, que dispara uma Thread,

  • que fica aguardando até que alguém diga o número de linhas a serem lidas.

  • Esse número de linhas será obtido através do parse de um arquivo, que será

  • feito pela classe Calculator.

  • A classe calculator, informa a todos os Readers, quantas linhas devem ser

  • lidas.
    */
    public class Sample
    {
    static class Reader implements Runnable {
    private int lines = 0;

     /**
      * A thread leitora deve esperar até que existam linhas a serem lidas.
      */
     public synchronized void waitLineNumber() {
         try { 
             while (lines &lt= 0) { //Olha a condição antes do wait aqui.
                 wait();
             }
         }
         catch (InterruptedException e) {
             System.out.println(&quot;Processamento da leitura interrompido&quot;);
         }
     }
    
     /**
      * Aqui deixamos outras threads definirem quantas linhas serão lidas e
      * acordamos o Reader.
      */
     public synchronized void setLinesToRead(int number) {
         lines = number;
         notifyAll();
     }
    
     public void run() {
         System.out.println(&quot;Aguardando número de linhas.&quot;);
         waitLineNumber();
         System.out.println(lines + &quot; linhas lidas!&quot;);
     }
    

    }

    /**

    • Agora vem nossa classe calculadora. Ele realiza o cálculo e avisa uma

    • série de readers.
      */
      static class Calculator implements Runnable {
      private Set<Reader> readers = new HashSet<Reader>();

      public void addReader(Reader reader) {
      readers.add(reader);
      }

      public void run() {
      // Parseamos o arquivo (de mentirinha aqui)
      int total = 0;
      for (int i = 0; i &lt 100; i++) {
      total += 1;
      }

       // Informamos aos readers quantas linhas devem ser lidas para que
       // possam fazer o seu trabalho.
       for (Reader r : readers) {
           r.setLinesToRead(total);
       }
      

      }
      }

    private static void startReader(Calculator c) {
    Reader r = new Reader();
    c.addReader®;
    new Thread®.start();
    }
    public static void main(String args[]) {
    Calculator c = new Calculator();

     //Iniciamos os três readers
     startReader(c);
     startReader(c);
     startReader(c);
     
     //Iniciamos a calculadora.
     new Thread(c).start();
    

    }
    }[/code]

No exemplo acima, fique atento aos seguintes detalhes:

  1. O reader sozinho é responsável por sincronizar-se. O código fica muito mais claro, pois o objeto de sincronização (nesse caso this) está explicito em todo lugar;
  2. O reader não assume qualquer coisa sobre como a sincronização é feita pela classe Calculator;
  3. Foi priorizado a clareza do código, sobretudo sobre as regras de negócio.

[code]/**

  • Nesse exemplo, Sample é a classe mediadora. Note que não é nossa intenção

  • usar o Reader nem a Calculadora externamente.

  • A variável lock foi criada para definir nosso objeto de sincronização e para

  • tornar o código mais claro.

  • A variável lines é compartilhada pelas duas threads, por isso seu acesso deve

  • ser sincronizado. Note como há muito mais dependência entre as classes aqui.

  • Esse exemplo é mais próximo do mecanismo que você utilizou.
    */
    public class Sample {
    private int[] lock = new int[0];
    private int lines = 0;

    class Reader implements Runnable {
    /**
    * A thread leitora deve esperar até que existam linhas a serem lidas.
    */
    public void waitLineNumber() {
    synchronized (lock) {
    try {
    while (lines &lt= 0) {
    lock.wait(); // Será disparado pela calculadora
    }
    }
    catch (InterruptedException e) {
    System.out.println("Processamento da leitura interrompido");
    }
    }
    }

     public void run() {
         System.out.println(&quot;Aguardando número de linhas.&quot;);
         waitLineNumber();
         System.out.println(lines + &quot; linhas lidas!&quot;);
     }
    

    }

    /**

    • Agora vem nossa classe calculadora. Ele realiza o cálculo e avisa uma

    • série de readers.
      */
      class Calculator implements Runnable {
      private Set<Reader> readers = new HashSet<Reader>();

      public void addReader(Reader reader) {
      readers.add(reader);
      }

      public void run() {
      // Parseamos o arquivo (de mentirinha aqui)
      int total = 0;
      for (int i = 0; i &lt 100; i++) {
      total += 1;
      }

       // Vamos avisar os readers
       synchronized (lock) {
           // Lines é usado nas duas threads, seu set e get deve ser
           // sincronizado.
           lines = total;
           lock.notifyAll();
       }
      

      }
      }

    private void startReader(Calculator c) {
    Reader r = new Reader();
    c.addReader®;
    new Thread®.start();
    }

    public Sample() {
    Calculator c = new Calculator();

     // Iniciamos os três readers
     startReader(c);
     startReader(c);
     startReader(c);
    
     // Iniciamos a calculadora.
     new Thread(c).start();
    

    }

    public static void main(String args[]) {
    new Sample();
    }
    }[/code]