Problemas com pausas e inicios de Threads [RESOLVIDO]

24 respostas
W

Ola pessoas, boa tarde

Estou com problemas e ‘continuar’ uma Thread pausada com o metodo wait();, comumente o metodo notifyAll(); botaria a Thread para funcionar novamente…
mas não é isso que acontece, segue meu código abaixo /

//Meu frame Runnable
public class ControleFrame extends JFrame implements Runnable {
//atributos..
//componentes de tela..

//Meu metodo Run
public void run() {
		try {
			
			String telaControle = Thread.currentThread().getName();

			int prodDiaINT = 0;
			int bobinaINT = Integer.parseInt(txtBobina.getText());
			int minBobinaINT = Integer.parseInt(txtMinBobina.getText());
			
			while(True){
				Thread.sleep(1000);
				prodDiaINT++;
				bobinaINT--;
				
				String prodDiaSTR = Integer.toString(prodDiaINT);
				String bobinaSTR = Integer.toString(bobinaINT);
				
				txtprodDia.setText(prodDiaSTR);
				txtBobina.setText(bobinaSTR);
				
				if(bobinaINT == minBobinaINT){
					txtBobina.setBackground(Color.red);
				}
                                //Nesse if eu mando a Thread parar quanto a caixa de texto bobina chega a 0
                                if(bobinaINT == 0){
					JOptionPane.showMessageDialog(null, "Favor trocar a bobina!!");
					System.out.println("PAROU");
					telaControle.wait();//METODO PARA PARAR THREAD
				}
			}
			
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

Isso é apenas um laço de repetição infinito só para incrementar e decrementar valor nas caixas que eu cito.

Reparem naquele if que mando a Thread parar, ate ai funciona. O problema é iniciar essa Thread novamente, eu ja tentei fazer outro if
para verificar quando e maior que zero eu chamo o notifyAll() mas nada acontece, acredito que seja pro causa do próprio laço. Eu tambem ja tentei
chamar o notityAll() no botao de OK do meu JDialog, responsavel por preencher um novo valor quando chega a 0 e a Thread para.
Enfim, tem alguma coisa muito errada ai, gostaria que me ajudassem.

Agradeço

24 Respostas

E

Não é por nada não, mas normalmente você não deve trabalhar com wait/notify/notifyAll a menos que seja um trabalho de escola, já que é extremamente difícil fazer isso funcionar direito.
Uma coisa que nunca vi, por exemplo, é justamente ficar pausando e reiniciando uma thread.
Na prática, usa-se alguma classe do pacote java.util.concurrent.

T

Threads com Swing? É, é meio chato mesmo.
Veja:
http://www.guj.com.br/java/283077-controle-de-threads-no-swingresolvido

entanglement

Estou justamente tentando fazer essa proeza. Se conseguir resultados “satisfatórios”, posto aqui (sim, é um trabalho “de escola” :smiley: )

Abraços.

W

entanglement:
Não é por nada não, mas normalmente você não deve trabalhar com wait/notify/notifyAll a menos que seja um trabalho de escola, já que é extremamente difícil fazer isso funcionar direito.
Uma coisa que nunca vi, por exemplo, é justamente ficar pausando e reiniciando uma thread.
Na prática, usa-se alguma classe do pacote java.util.concurrent.

Que é extremamente dificil trabalhar com esses metodos eu ja percebi, mas se não devo usalos … tipo nas video aulas e turtoriais que vi e tenho, nunca vi esse pacote java.util.concurrent
a questão agr é que embaralho mais ainda kk :X mas vlw pela atenção

W

TerraSkilll:
Threads com Swing? É, é meio chato mesmo.
Veja:
http://www.guj.com.br/java/283077-controle-de-threads-no-swingresolvido

entanglement

Estou justamente tentando fazer essa proeza. Se conseguir resultados “satisfatórios”, posto aqui (sim, é um trabalho “de escola” :smiley: )

Abraços.

kk, vlw pela atenção tb mas esse topico ai não sei se vc viu, mas fui eu mesmo que criei, la o meu objetivo era apenas pausar uma thread.

W

Enfim eu gostaria de quem alguem me desse um pontapé nesse assunto, qual a melhor forma de manipular threads em swing se baseando no meu
codigo acima?se não devo usar os metodos wait/notify/notifyAll, quais eu devo usar? pacote? api? usar o que o entanglement falou, java.util.current? (um pequeno
exemplo sobre isso).

V
Tem muita coisa estranha no seu código:

a) Você está dando wait numa String, retornada pelo método getName();

b) Você não está dando wait() num trecho de código synchronized;

c) Você não postou o local onde está dando o notifyAll();

d) Seu wait() não está dentro de um loop, o que pode deixa-lo sujeito a spurious wakeups().
R

Fiz um exemplo aqui para tentar ajudar:

package testethread;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;



public class TesteThread extends JFrame{

	private JLabel label;
	private JButton cmd;
	private ExecutorService exec;
	private Future<?> f;
	
	public TesteThread(){
		getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
		label = new JLabel("Thread parada!");
		cmd = new JButton("Inicializar Thread");
		exec = Executors.newSingleThreadExecutor();
		
		cmd.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				//Aqui eu não preciso chamar SwingUtilities.invoke... porque já estará automaticamente na thread de eventos do swing
				if(f != null) {
					f.cancel(true);
					f = null;
					label.setText("Thread parada!");
					cmd.setText("Inicializar Thread");
				} else {
					label.setText("");
					cmd.setText("Parar Thread");
					f = exec.submit( new ThreadUpdateLabel(label) );
				}
			}
			
		});
		
		add(label);
		add(cmd);
		
		setSize(250, 200);
		setLocationRelativeTo(null);
		
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				if(exec != null) {
					exec.shutdownNow();
				}
                                System.exit(0);
			}
			
		});
	}
	
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable(){

			@Override
			public void run() {
				new TesteThread().setVisible(true);
			}
			
		});
		
	}
}


class ThreadUpdateLabel implements Runnable{

	private JLabel labelParaAtualizar = null;
	
	
	public ThreadUpdateLabel(JLabel label)
	{
		if(label == null) throw new IllegalArgumentException("O label deve ser != null!");
		this.labelParaAtualizar = label;
	}
	
	@Override
	public void run() {
		long contador = 0;
		
		while(!Thread.currentThread().isInterrupted()) {
			
			try {
				SwingUtilities.invokeAndWait( //Preciso fazer isso para que o componente visual seja atualizado na thread de eventos do swing
						new UpdateLabel(contador, labelParaAtualizar)
				);
				Thread.sleep(1000);
				if(Long.MAX_VALUE == contador) {
					contador = 0;
				}
				contador++;
			} catch (InterruptedException e1) {
				System.out.println("(" + Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + ") foi interrompida enquanto estava em sleep!");
				Thread.currentThread().interrupt(); //cancelando a thread corrente
				//e1.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			} 
		}
		
		System.out.println("(" + Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + ") será finalizada!");
	}
	
	
}

class UpdateLabel implements Runnable {
	private long contador;
	private JLabel labelParaAtualizar = null;

	public UpdateLabel(long contador, JLabel labelParaAtualizar) {
		super();
		this.contador = contador;
		this.labelParaAtualizar = labelParaAtualizar;
	}

	@Override
	public void run() {
		labelParaAtualizar.setText("Thread executando há " + contador + " segundos!");
	}

}

[]'s

W
<blockquote><div class="quote-author">ViniGodoy:</div>Tem muita coisa estranha no seu código:

a) Você está dando wait numa String, retornada pelo método getName();

b) Você não está dando wait() num trecho de código synchronized;

c) Você não postou o local onde está dando o notifyAll();

d) Seu wait() não está dentro de um loop, o que pode deixa-lo sujeito a spurious wakeups().</blockquote>

Realmente Vini em relação a esses erros, realmente foi falha minha ao postar aqui no fórum pois já havia corrigido. Muito obrigado pela atenção vou
ir resolvendo aqui e depois retorno

W

rogeriopaguilar

Valeu mesmo cara nem sei como agradecer, vou resolvendo aqui e depois retorno se deu certo, Obrigado!!

E

Pois é, material em português sobre java.util.concurrent é meio difícil arranjar mesmo :frowning:

(Confesso que nunca vi uma video-aula de Java, nem sei o que o pessoal ensina nelas.)

W

rogeriopaguilar:
Fiz um exemplo aqui para tentar ajudar:

package testethread;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;



public class TesteThread extends JFrame{

	private JLabel label;
	private JButton cmd;
	private ExecutorService exec;
	private Future<?> f;
	
	public TesteThread(){
		getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
		label = new JLabel("Thread parada!");
		cmd = new JButton("Inicializar Thread");
		exec = Executors.newSingleThreadExecutor();
		
		cmd.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				//Aqui eu não preciso chamar SwingUtilities.invoke... porque já estará automaticamente na thread de eventos do swing
				if(f != null) {
					f.cancel(true);
					f = null;
					label.setText("Thread parada!");
					cmd.setText("Inicializar Thread");
				} else {
					label.setText("");
					cmd.setText("Parar Thread");
					f = exec.submit( new ThreadUpdateLabel(label) );
				}
			}
			
		});
		
		add(label);
		add(cmd);
		
		setSize(250, 200);
		setLocationRelativeTo(null);
		
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				if(exec != null) {
					exec.shutdownNow();
				}
                                System.exit(0);
			}
			
		});
	}
	
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable(){

			@Override
			public void run() {
				new TesteThread().setVisible(true);
			}
			
		});
		
	}
}


class ThreadUpdateLabel implements Runnable{

	private JLabel labelParaAtualizar = null;
	
	
	public ThreadUpdateLabel(JLabel label)
	{
		if(label == null) throw new IllegalArgumentException("O label deve ser != null!");
		this.labelParaAtualizar = label;
	}
	
	@Override
	public void run() {
		long contador = 0;
		
		while(!Thread.currentThread().isInterrupted()) {
			
			try {
				SwingUtilities.invokeAndWait( //Preciso fazer isso para que o componente visual seja atualizado na thread de eventos do swing
						new UpdateLabel(contador, labelParaAtualizar)
				);
				Thread.sleep(1000);
				if(Long.MAX_VALUE == contador) {
					contador = 0;
				}
				contador++;
			} catch (InterruptedException e1) {
				System.out.println("(" + Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + ") foi interrompida enquanto estava em sleep!");
				Thread.currentThread().interrupt(); //cancelando a thread corrente
				//e1.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			} 
		}
		
		System.out.println("(" + Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + ") será finalizada!");
	}
	
	
}

class UpdateLabel implements Runnable {
	private long contador;
	private JLabel labelParaAtualizar = null;

	public UpdateLabel(long contador, JLabel labelParaAtualizar) {
		super();
		this.contador = contador;
		this.labelParaAtualizar = labelParaAtualizar;
	}

	@Override
	public void run() {
		labelParaAtualizar.setText("Thread executando há " + contador + " segundos!");
	}

}

[]'s

Esse seu exemplo está funcionando perfeitamente, e também apliquei no meu projeto está funcionando também. Porém tenho mais uma duvida.
No seu projeto a Thread Inicializa e para, inicializa e para quantas vezes vc quiser certo, mas por exemplo (talvez eu esteja equivocado ao interpretar certos métodos…)
eu preciso pausar a Thread quando o valor x da caixa de texto x chega a 0 e depois de eu inserir outro valor(com a Thread parada) eu iniciar essa mesma Thread exatamente
fazendo a mesma coisa, só que com o valor que eu preenchi novo é claro… com um JDialog por exemplo …?!

R

O ciclo de vida da thread (parar e recomeçar) é controlado por aquele objeto future:

if(f != null) { // Se a thread estiver executando, interrompe ela chamando o método cancel  
                    f.cancel(true);  
                    f = null;  
                    //...
                } else {  
                   //...   
                    f = exec.submit( new ThreadUpdateLabel(label) );  //Inicializa ela de novo
                }  
            }

Você pode utilizar essa mesma lógica em qualquer evento para parar ou iniciar a thread de novo, como por exemplo
em algum evento da caixa de texto. O único cuidado é ao utilizar a variável f. Para não ter que sincronizar o acesso
a ela manualmente, todas as vezes que você utilizar essa variável você tem que de alguma forma fazer com que esse
acesso seja feito na thread de eventos do swing. Os listeners de eventos do swing já são executados na thread de eventos do swing, então
você pode acessar a variável sem problemas. Se for necessário utilizar em algum outro local a variável f é necessário utilizar SwingUtilities.invokeAndWait ou SwingUtilities.invokeLater (como eu fiz lá no começo do código para atualizar o label) e utilizar a referência à variável f dentro do método run do runnable passado aos parâmetros desses métodos. Como a thread de eventos do swing executa os eventos um a um então não é necessário
sincronizar o acesso à variável f, desde que sejam seguidas as regras acima. Se não quiser seguir estas regras então será necessário sincronizar
o acesso à variável f através de algum lock todas as vezes que for executar algum método ou verificação em f.

R

Outra coisa que esqueci de falar, se você precisar que a thread cancele ela mesma dentro do método run, pode chamar o método Thread.currentThread().interrupt(); para que na próxima execução do while a thread pare de executar. A referência à variável f ainda existirá no programa , mas é possível verificar se f foi cancelado utilizando o método isCancelled do objeto future ou o método isDone para verificar se a thread terminou sua execução.

W

Sim estou entendo a lógica e por enquanto ta dando certo, mas não to conseguindo entender de como vou interagir com outra Classe(JDialog),
vou postar partes do meu código abaixo

//Meu JFrame principal ( Onde começa tudo)
public class ProdutoFrame extends JFrame{
//atributos..

private ExecutorService exec;
private Future<?> f;

//constructor e componentes de tela..

//Meu Botao onde eu Inicio a segunda tela ControleFrame , a Thread
btnIniciar.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent a) {
				dispose();
				if(f != null){
					f.cancel(true);
					f = null;
					btnIniciar.setText("Iniciar");
				}else{
					f = exec.submit(new ControleFrame());
				}
			}
		});

//Minha Classe ControleFrame
public class ControleFrame extends JFrame implements Runnable{

//atributos
//constructor e componentes de tela

//Metodo Run
public void run() {
		
		long contDia = 0 ;
		long contBobina = Integer.parseInt(txtBobina.getText());
		
		while(!Thread.currentThread().isInterrupted()){
			try {
				SwingUtilities.invokeAndWait(new UpdateTexts(contDia, contBobina, txtprodDia, txtBobina));
				Thread.sleep(1000);
				if(Long.MAX_VALUE == contDia){
					contDia = 0;
				}
				contDia++;
				contBobina--;
				if(contBobina == 0){
					Thread.currentThread().interrupt();
				}
			} catch (Exception e) {
			}
		}
		
	}
//Repare que mando  a Thread parar quando o valor da caixa de texto bobina chega a 0, até ai tudo bem


//classe UpdateTexts
class UpdateTexts implements Runnable{
		
		private long cntDia, cntBobina;
		private JTextField atualizarTextDia, atualizarBobina;
		
		public UpdateTexts(long contDia, long cntBobina, JTextField atualizartxtDia, 
				JTextField atualizarBobina) {
			super();
			this.cntDia = contDia;
			this.cntBobina = cntBobina;
			this.atualizarTextDia = atualizartxtDia;
			this.atualizarBobina = atualizarBobina;
		}

		@Override
		public void run() {
			atualizarTextDia.setText(""+cntDia);
			atualizarBobina.setText(""+cntBobina);
		}
		
	}

Ai no caso com a Thread parada, eu tenho um Botão abaixo da caixa de texto txtBobina que ao clicar abre um JDialog responsável por preencher um valor NOVO
para a caixa de texto txtBobina ao clicar Ok, ai está minha duvida de como vou reiniciar a Thread nesse caso, mas tem um porém, a Thread não poderia recomeçar do 0
porque se não a caixa de texto onde esta setando cntDia também iria zerar e recomeçar do 0, e isso não pode acontecer. Minha ideia é a Thread continuar da onde ela parou (não sei se isso é possivel), ai após citar o Novo valor continuar decrementando como estava e o valor da cntDia continuar incrementando do Mesmo valor que parou, entende ?!
Resumindo em poucas palavras, eu quero que a Thread apenas Pause e depois possa continuar da onde ela parou. Ai se der, aonde eu posso fazer essa verificação após preencher o novo valor e iniciar a Thread novamente?

Se necessário eu posso postar o código do JDialog.

W

A esqueci, obrigado pela força

R

Acho que uma forma fácil de resolver esse caso é retirar o método run da classe jframe, criar uma outra classe que implemente runnable, tirar os campos
contDia e contBobina do método run e declarar eles nessa classe nova e criar um construtor que receba estes valores. Desta forma, todas as vezes que você quiser criar de novo a thread você passa os valores que quiser no construtor antes de submeter a thread ao pool chamando o método submit. Assim a thread começará com os valores que você definir no construtor.

W

rogeriopaguilar:
Acho que uma forma fácil de resolver esse caso é retirar o método run da classe jframe, criar uma outra classe que implemente runnable, tirar os campos
contDia e contBobina do método run e declarar eles nessa classe nova e criar um construtor que receba estes valores. Desta forma, todas as vezes que você quiser criar de novo a thread você passa os valores que quiser no construtor antes de submeter a thread ao pool chamando o método submit. Assim a thread começará com os valores que você definir no construtor.

Tais dizendo pra tira o metodo Run da classe ControleFrame? mas ainda vou precisa que a classe ControleFrame implemente Runnable nao é? Tipo mas não vai dar no mesmo fazendo isso?

R

Eu modifiquei aquele exemplo inicial para tentar ilustrar melhor o que tentei explicar. Agora a variável contador será compartilhada entre a thread principal e a thread que atualiza este valor. Quando você clicar no botão parar thread o último valor estará nessa variável compartilhada, então quando você inicializar de novo a thread o último valor será utilizado. Coloquei um botão para zerar este valor. Adicionei alguns comentários para tentar explicar melhor.

package teste.concorrencia.swing;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;



public class TesteThread extends JFrame{

	private JLabel label;
	private JButton cmd;
	private JButton cmdZerarContador;
	private ExecutorService exec;
	private Future<?> f;
	/*
	 * Aqui eu guardo o valor do contador ao invés de utilizar uma variável long no método run
	 * da thread. Este contador será compartilhado pela thread principal do programa e pela
	 * thread que nós criamos para atualizá-lo.
	 * Utilizei o tipo atomiclong porque a variável será compartilhada entre threads diferentes.
	 * Para outros tipos de dados poderíamos utilizar métodos get e set com o midificador
	 * synchronized
	 */
	private final AtomicLong contador = new AtomicLong(0); 
	
	public TesteThread(){
		getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
		label = new JLabel("Thread parada!");
		cmd = new JButton("Inicializar Thread");
		exec = Executors.newSingleThreadExecutor();
		cmdZerarContador = new JButton("Zerar o contado");
		cmdZerarContador.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				contador.set(0); //Aqui eu zero o contador
			}
		});
		
		cmd.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				//Aqui eu não preciso chamar SwingUtilities.invoke... porque já estará automaticamente na thread de eventos do swing
				if(f != null) {
					f.cancel(true);
					f = null;
					label.setText("Thread parada!");
					cmd.setText("Inicializar Thread");
				} else {
					label.setText("");
					cmd.setText("Parar Thread");
					//Aqui eu passo o último valor do contador desde a última chamada da thread
					f = exec.submit( new ThreadUpdateLabel(label, contador ) );
				}
			}
			
		});
		
		add(label);
		add(cmd);
		add(cmdZerarContador);
		
		setSize(250, 200);
		setLocationRelativeTo(null);
		
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				if(exec != null) {
					exec.shutdownNow();
				}
                                System.exit(0);
			}
			
		});
	}
	
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable(){

			@Override
			public void run() {
				new TesteThread().setVisible(true);
			}
			
		});
		
	}
}

/**
 * Aqui eu modifiquei para receber o contador como parâmetro
 * */
class ThreadUpdateLabel implements Runnable{

	private JLabel labelParaAtualizar = null;
	/* Aqui eu declarei o contador e retirei do método run	
	 * O contador é o mesmo objeto criado pela classe jframe, ou seja, eles
	 * são compartilhados entre as threads. Com isso eu consigo manter
	 * o último valor utilizado
	 */
	private AtomicLong contador; 
	
	/**
	 * Adicionei o parâmetro no construtor da classe, para que o contador possa ser inicializado com
	 * um valor diferente de zero
	 * */
	public ThreadUpdateLabel(JLabel label, AtomicLong contador)
	{
		if(label == null) throw new IllegalArgumentException("O label deve ser != null!");
		this.labelParaAtualizar = label;
		this.contador = contador;
	}
	
	@Override
	public void run() {
		
		while(!Thread.currentThread().isInterrupted()) {
			
			try {
				SwingUtilities.invokeAndWait( //Preciso fazer isso para que o componente visual seja atualizado na thread de eventos do swing
						new UpdateLabel(contador.get(), labelParaAtualizar)
				);
				Thread.sleep(1000);
				contador.incrementAndGet(); //somo um ao contador
			} catch (InterruptedException e1) {
				System.out.println("(" + Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + ") foi interrompida enquanto estava em sleep!");
				Thread.currentThread().interrupt(); //cancelando a thread corrente
				//e1.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			} 
		}
		
		System.out.println("(" + Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + ") será finalizada!");
	}
	
	
}

class UpdateLabel implements Runnable {
	private long contador;
	private JLabel labelParaAtualizar = null;

	public UpdateLabel(long contador, JLabel labelParaAtualizar) {
		super();
		this.contador = contador;
		this.labelParaAtualizar = labelParaAtualizar;
	}

	@Override
	public void run() {
		labelParaAtualizar.setText("Thread executando há " + contador + " segundos!");
	}

}
W
rogeriopaguilar:
Eu modifiquei aquele exemplo inicial para tentar ilustrar melhor o que tentei explicar. Agora a variável contador será compartilhada entre a thread principal e a thread que atualiza este valor. Quando você clicar no botão parar thread o último valor estará nessa variável compartilhada, então quando você inicializar de novo a thread o último valor será utilizado. Coloquei um botão para zerar este valor. Adicionei alguns comentários para tentar explicar melhor.
package teste.concorrencia.swing;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;



public class TesteThread extends JFrame{

	private JLabel label;
	private JButton cmd;
	private JButton cmdZerarContador;
	private ExecutorService exec;
	private Future<?> f;
	/*
	 * Aqui eu guardo o valor do contador ao invés de utilizar uma variável long no método run
	 * da thread. Este contador será compartilhado pela thread principal do programa e pela
	 * thread que nós criamos para atualizá-lo.
	 * Utilizei o tipo atomiclong porque a variável será compartilhada entre threads diferentes.
	 * Para outros tipos de dados poderíamos utilizar métodos get e set com o midificador
	 * synchronized
	 */
	private final AtomicLong contador = new AtomicLong(0); 
	
	public TesteThread(){
		getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
		label = new JLabel("Thread parada!");
		cmd = new JButton("Inicializar Thread");
		exec = Executors.newSingleThreadExecutor();
		cmdZerarContador = new JButton("Zerar o contado");
		cmdZerarContador.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				contador.set(0); //Aqui eu zero o contador
			}
		});
		
		cmd.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				//Aqui eu não preciso chamar SwingUtilities.invoke... porque já estará automaticamente na thread de eventos do swing
				if(f != null) {
					f.cancel(true);
					f = null;
					label.setText("Thread parada!");
					cmd.setText("Inicializar Thread");
				} else {
					label.setText("");
					cmd.setText("Parar Thread");
					//Aqui eu passo o último valor do contador desde a última chamada da thread
					f = exec.submit( new ThreadUpdateLabel(label, contador ) );
				}
			}
			
		});
		
		add(label);
		add(cmd);
		add(cmdZerarContador);
		
		setSize(250, 200);
		setLocationRelativeTo(null);
		
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				if(exec != null) {
					exec.shutdownNow();
				}
                                System.exit(0);
			}
			
		});
	}
	
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable(){

			@Override
			public void run() {
				new TesteThread().setVisible(true);
			}
			
		});
		
	}
}

/**
 * Aqui eu modifiquei para receber o contador como parâmetro
 * */
class ThreadUpdateLabel implements Runnable{

	private JLabel labelParaAtualizar = null;
	/* Aqui eu declarei o contador e retirei do método run	
	 * O contador é o mesmo objeto criado pela classe jframe, ou seja, eles
	 * são compartilhados entre as threads. Com isso eu consigo manter
	 * o último valor utilizado
	 */
	private AtomicLong contador; 
	
	/**
	 * Adicionei o parâmetro no construtor da classe, para que o contador possa ser inicializado com
	 * um valor diferente de zero
	 * */
	public ThreadUpdateLabel(JLabel label, AtomicLong contador)
	{
		if(label == null) throw new IllegalArgumentException("O label deve ser != null!");
		this.labelParaAtualizar = label;
		this.contador = contador;
	}
	
	@Override
	public void run() {
		
		while(!Thread.currentThread().isInterrupted()) {
			
			try {
				SwingUtilities.invokeAndWait( //Preciso fazer isso para que o componente visual seja atualizado na thread de eventos do swing
						new UpdateLabel(contador.get(), labelParaAtualizar)
				);
				Thread.sleep(1000);
				contador.incrementAndGet(); //somo um ao contador
			} catch (InterruptedException e1) {
				System.out.println("(" + Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + ") foi interrompida enquanto estava em sleep!");
				Thread.currentThread().interrupt(); //cancelando a thread corrente
				//e1.printStackTrace();
			} catch (Exception e1) {
				e1.printStackTrace();
			} 
		}
		
		System.out.println("(" + Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + ") será finalizada!");
	}
	
	
}

class UpdateLabel implements Runnable {
	private long contador;
	private JLabel labelParaAtualizar = null;

	public UpdateLabel(long contador, JLabel labelParaAtualizar) {
		super();
		this.contador = contador;
		this.labelParaAtualizar = labelParaAtualizar;
	}

	@Override
	public void run() {
		labelParaAtualizar.setText("Thread executando há " + contador + " segundos!");
	}

}

Beleza rogerio ta funcionando quase certo aqui, só tem um ultimo probleminha.. Ali no lugar no seu contador.incrementAndGet() no meu projeto estou usando o decrementAndGet(),
ai logo abaixo eu faço um if para que quando chegue a zero eu paro >> Thread.currentThread().interrupt(), tipo beleza quando chega a zero a Thread para, mas o contador continua decrementando, ou seja, ele interrompeu a Thread mas continuo passando no laço. Algo que eu tenha esquecido de fazer será? já depurei, olhei e olhei seu exemplo e meu código e não achei nada de anormal, não descobri porque continua executando o laço.

W

Ae mano, descobri meu problema kk, meu JOptionpane.showMessageDialog que tava dando esse problema, após eu clicar ok ele continuava executando o laço

R

Entendi. Eu achei estranho quando vc disse que continuava mesmo depois de parar a thread.
Bom, acho que agora funcionou então…
[]'s

W

rogeriopaguilar:
Entendi. Eu achei estranho quando vc disse que continuava mesmo depois de parar a thread.
Bom, acho que agora funcionou então…
[]'s

Sim ta funcionando certinho, depois de apanhar bastante funcionou kk, mas obrigado por tudo, o tempo que tu perdeu, a paciencia… hehe
E tenho ctz que esse tópico vai ajuda muita gente iniciantes em swing com threads como eu. Abraço

R

Não tem problema não. Thread é meio chato de aprender mesmo, seja no swing ou não. Aliás, no swing existe outra forma de utilizar threads, que é criada especificamente para utilizar com o swing. Quando der dá uma lida no javadoc da classe swingworker:

http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html

Mas da forma que fizemos eu acho melhor pra entender como funciona “por baixo dos panos”.

Até mais.

W

rogeriopaguilar:
Não tem problema não. Thread é meio chato de aprender mesmo, seja no swing ou não. Aliás, no swing existe outra forma de utilizar threads, que é criada especificamente para utilizar com o swing. Quando der dá uma lida no javadoc da classe swingworker:

http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html

Mas da forma que fizemos eu acho melhor pra entender como funciona “por baixo dos panos”.

Até mais.

Vo da uma olhada no docs sim, vlw, é melhor mesmo sempre fazer “por baixo dos panos” primeiro e entender todo o processo.

Criado 2 de outubro de 2012
Ultima resposta 7 de out. de 2012
Respostas 24
Participantes 5