Thread com comportamento inesperado

3 respostas
Fox_McCloud

Senhores, bom dia.

Estou tentando me aprofundar em Threads, mas ainda me embanano com wait() e notify().

No meu teste, quando eu tento pausar a Thread, a Thread continua funcionando e o JFrame congela!

:shock:

Estou postando o código, é pequeno, só o básico, e eu peço a gentileza de alguém que conhece Threads me dar umas dicas do uso de wait() e notify()...

Notem que na minha classe interna eu utilizei Rodavel.this a fim de obter a tranca (lock) da instância da classe interna, e não da externa... assim mesmo não funciona e eu não entendo onde estou errando...

Obrigado desde já!

:wink:

import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JButton;
import javax.swing.JFrame;

public class T1 extends JFrame {

	private JButton pausa;
	private JButton continua;
	private Thread t;
	private Rodavel r;
	
	public T1(){
		super("Threads Test");
		initThread();
		initWindow();
	}
	
	private void initThread() {
		r = new Rodavel();
		t = new Thread(r);
		t.start();
	}

	private void initWindow(){
		pausa = new JButton("Pausa");
		continua = new JButton("Continua");
		pausa.addMouseListener(new MouseAdapter(){
			public void mouseClicked(MouseEvent e) {
				r.pausa();
			}
		});
		continua.addMouseListener(new MouseAdapter(){
			public void mouseClicked(MouseEvent e) {
				r.continua();
			}
		});
		this.setLayout(new FlowLayout());
		this.add(pausa);
		this.add(continua);
		this.pack();
		this.setResizable(false);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	
	private class Rodavel implements Runnable{
		public void run(){
			while(true){
				System.out.println("tick");
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {}
			}
		}
		public void pausa(){
			synchronized(Rodavel.this){
				try {
					Rodavel.this.wait();
				} catch (InterruptedException e) {}
			}
		}
		public void continua(){
			synchronized(Rodavel.this){
				Rodavel.this.notify();
			}
		}
	}
	
	public static void main(String[] args) {
		new T1();
	}
	
}

3 Respostas

ViniGodoy

Você está confundindo as coisas.

A thread não é um objeto. A thread é a linha de execução.

Agora, eu te pergunto… qual foi a linha de execução que chamou o método wait()? Foi a da thread que estava rodando em paralelo, ou foi a do Swing? Foi a do Swing. Por isso, quem parou foi o Swing.

Para corrigir o seu código, a thread do Swing tem que simplesmente avisar a thread criada no start, para que ela mesmo chame o wait.

Como nesse caso (refatorei sua classe Runnable para ser usada no mesmo contexto anterior. Não testei esse código, mas a idéia básica é essa):

private class Rodavel implements Runnable{   
   private boolean pausa = false;

   public void run(){   
      while(true){   
         System.out.println("tick");   
         try {   
            Thread.sleep(500);   
            synchronized (this) {
               //O wait precisa estar associado a um while
               //senão há riscos de spurious wakeup.
               while (pausa)  //Testa continuamente essa variável, que pode ser alterada por outras threads.
                  wait();   
            }
         } catch (InterruptedException e) {}   
      }
   }   
                
   private synchronized void pausa() {
      pausa = true; //A thread do run logo irá ler essa variável, que será definida pela thread do swing. 
   }

   public synchronized void continua(){   
      pausa = false; //Avisa a thread do runnable que pode rodar
      notify();          //E pede para o SO a colocar em execução em algum momento no futuro.
   }   
}

Agora… imagine que tem uma thread rodando dentro do seu run… É um outro programinha, executando passo-a-passo, imprimindo tick, mas contido dentro daquele while. Esse programinha lê a variável pausa, a vai chamar o método wait se ela valer true.

Existe outro mini programa, que é o Swing, controlando ações do usuário. Ele vai lá e altera a variável pausa, que é a mesma que aquele programinha do run lê. Por isso dizemos que threads compartilham memória, já que a variável, no fundo, é um espaço de memória com um nome, visto por duas threads. A thread do run(), então, vai testar o novo valor de pausa e chamar o método wait().

Do jeito que você estava fazendo, a thread do Swing entra no método pausa() e chama o wait(). A thread do run() continua rodando normalmente, já que ela nem sequer chamou método nenhum.

Fox_McCloud

Hum… agora fez mais sentido… obrigado pela resposta!

:roll:

Em que outro contexto eu posso usar o wait()? Eu sei que eu posso usar a tranca de outro objeto, por exemplo…

ViniGodoy

No famoso algoritmo do produtor/consumidor, por exemplo.

Imagine uma classe que contém uma fila. Uma thread lê dados dessa fila, outra coloca dados nessa fila.
Você pode usar o wait para fazer quem lê esperar ter elementos na fila, caso não tenha. E o notify() para o produtor avisar que um novo elemento foi colocado na fila.

Criado 20 de março de 2008
Ultima resposta 20 de mar. de 2008
Respostas 3
Participantes 2