JButton + Thread

Construí uma janela, com JButtons q servem para iniciar o processo, parar o processo e abrir um arquivo txt de log.
[color=red]Problemas:[/color]
Esses JButtons não funcionam como aparentemente deveriam…
[1]. O botão Start chama um método q está em outra classe, e esse método possui uma Thread t. Isso está ok.
Porém ele fica ‘travado’ e não consigo clicar nos outros botões, parece q ele está esperando a tarefa ser terminada para se ‘destravar’… :roll:

[2]. O botão Stop não consegue parar a Thread t, fiz o seguinte método para interromper a thread, mas não pára.

public static void Stop() { t = null; }

Alguém sabe como chamar e como interromper uma Thread com JButton?
Devo usar Runnable?
[]´s!

Tenta da um t.stop();

[EDIT]

Agora que li direito ali. Não tem como forçar uma thread a parar.
Você pode colocar uma variável de controle e então testar o valor dessa variável para interromper a thread.

Aqui nesse link, tem um programa que mostra um exemplo disso que falei ali em cima usando o método interrupt:
http://www.guj.com.br/posts/list/52481.java#276164

Eu prefiro ainda usar a variável de controle ao invés do interrupt. O interrupt eu deixo para coisas mais drásticas, geralmente, quando o próprio java chama o método na hora de matar a VM.

E, embora o exemplo do link mostre isso, não use ThreadGroups.
Prefira Callables e o ExecutorsService.

Kra, pois é, não sei o q está acontecendo, mas dá um erro…não sei se é pq eu mando a thread adormecer…
Os objetivos são dois: fazer o primeito botão chamar o método, e preencher a jTextarea da classe MainGui pela classe Tarefa.

Porém, nenhum dos dois acontece:

Classe da GUI:

package testsPack;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MainGui extends javax.swing.JFrame 
{		
	private static final long serialVersionUID = 1L;

	private static javax.swing.JButton jButton1, jButton2, jButton3;
	private static javax.swing.JScrollPane jScrollPane1;
	private static javax.swing.JScrollPane jScrollPane2;
	public static javax.swing.JTable jTable1;
	public static javax.swing.JTextArea jTextArea1;

	public MainGui() 
	{		
		initComponents();			
	}	    
	
	public static void main(String args[]) 
	{	    	
		java.awt.EventQueue.invokeLater(new Runnable() 
		{
			public void run() 
			{
				new MainGui().setVisible(true);
			}
		});
	}
		
	private void initComponents() 
	{	
		setTitle("WebService Consulta");
		setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

		jButton1 = new javax.swing.JButton();
		jButton1.setText("_Start_");	
		jButton1.addActionListener
		(
			new ActionListener()
			{		    
				public void actionPerformed(ActionEvent e1)
				{
					TarefaContinua.start();	
				}
			}
		);

		jButton2 = new javax.swing.JButton();
		jButton2.setText("_Stop_");
		jButton2.addActionListener
		(
			new ActionListener()
			{		    
				public void actionPerformed(ActionEvent e2)
				{
			    	TarefaContinua.stop();	
			    }
			}
		);	  

		jButton3 = new javax.swing.JButton();
		jButton3.setText("_Logs_");
		jButton3.addActionListener
		(
			new ActionListener()
			{		    
				public void actionPerformed(ActionEvent e3)
				{
					TarefaContinua.exit();	
				}
			}
		);	

		jScrollPane1 = new javax.swing.JScrollPane();
		jScrollPane2 = new javax.swing.JScrollPane();

		jTextArea1 = new javax.swing.JTextArea();
		jTextArea1.setColumns(20);
		jTextArea1.setRows(5);		
		jTextArea1.setEditable(false);
		jTextArea1.setBackground(Color.BLACK);
		jTextArea1.setDisabledTextColor(Color.ORANGE);		
		jTextArea1.setEnabled(false);		
				
		jScrollPane1.setViewportView(jTextArea1);	
			
		jTable1 = new javax.swing.JTable();
		jTable1.setEnabled(false);
		jTable1.setModel(new javax.swing.table.DefaultTableModel
		(
				new Object[][] 
				{ 
					{1, 111}, {2, 222}, {3, 333}, 
					{4, 444}, {5, 555}, {6, 666}
				},
				new String [] 
				{
					"Status", "Total"
				}
		)
	);
		jScrollPane2.setViewportView(jTable1);

//INICIO NAO SE PREOCUPE COM ISSO E APENAS O POSICIONAMENTO DOS 
//COMPONENTES GERADO AUTOMATICAMENTE PELO NETBEANS

		org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
		getContentPane().setLayout(layout);
		layout.setHorizontalGroup(
				layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
				.add(layout.createSequentialGroup()
						.addContainerGap()
						.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
								.add(layout.createSequentialGroup()
										.add(jScrollPane1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 460, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
										.addContainerGap())
										.add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
												.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
												.add(jScrollPane2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 375, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
												.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
												.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
														.add(jButton3)
														.add(jButton2)
														.add(jButton1))
														.add(31, 31, 31))))
		);
		layout.setVerticalGroup(
				layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
				.add(layout.createSequentialGroup()
						.add(19, 19, 19)
						.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
								.add(layout.createSequentialGroup()
										.add(jButton1)
										.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
										.add(jButton2)
										.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
										.add(jButton3))
										.add(jScrollPane2, 0, 0, Short.MAX_VALUE))
										.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
										.add(jScrollPane1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 300, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
										.add(15, 15, 15))
		);
		pack();		

//FIM NAO SE PREOCUPE COM ISSO E APENAS O POSICIONAMENTO DOS 
//COMPONENTES GERADO AUTOMATICAMENTE PELO NETBEANS
	}	
}

Classe da tarefa:

package testsPack;

import java.text.SimpleDateFormat;
import java.util.Date;

public class TarefaContinua
{	
	private static final long INTERVALO = 3600000;
	private static int CICLO = 1;	
	private static Thread t = new Thread();
	static MainGui mg = new MainGui();
			
	public static void start()
	{		
		try		
		{				
			SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy - hh:mm:ss");	
			
			t.start();			
			MainGui.jTextArea1.append("\nPROCESSO INICIADO...\n");
			
			Date d1 = new Date();
			mg.jTextArea1.append("INICIO: " + sdf.format(d1));		
								
			mg.jTextArea1.append("------------TAREFAAAAAAA------------");

			Date d2 = new Date();
			mg.jTextArea1.append("FIM: " + sdf.format(d2));					
										
			t.sleep(INTERVALO); //milisegundos 
			start();
		}
		catch(Exception e)
		{			
			e.printStackTrace();
		}
	}	
	
	public static void stop() {
		mg.jTextArea1.append("STOP");
		t = null;
	}	    		
	public static void exit() {		
		mg.jTextArea1.append("BYE BYE");
		System.exit(0);		
	}
}

Quando clico no Start ele congela junto com a Thread, e depois dá o seguinte erro:

Agradeço qq ajuda pessoal!

  1. O método stop() está deprecated;
  2. O método start() só pode ser chamado uma única vez objeto thread.

Não é assim que se faz.
Leia com atenção o link que eu te passei ali em cima (toda a discussão), pq nela eu explico a maneira correta de parar uma thread.

Aproveita e já lê esses aqui também:
http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html
http://www.guj.com.br/posts/list/57781.java#303957

Ah sim… e também dá uma olhada no artigo do Informit, sobre TableModel:
http://www.informit.com/articles/article.aspx?p=332278

Usar o DefaultTableModel é pedir para ter problemas.

Fala Vinnie…
O start() que eu chamo na ultima linha do método não se refere ao método start de Thread, mas ao próprio método que eu estou, ou seja, este método depois de executar o q tem q executar ele chama a sí proprio, isso sempre funcionou até eu ter que colocar esta GUI neste sistema.
Veja bem que eu chamo “start()” e não "t.start()"
Mas vou olhar novamente o tópico indicado por vc.
Obrigado.

Sim… mas na sua classe tarefa, o método start() da classe TarefaContinua chama start() novamente.
Isso vai causar que o objeto t tenha o seu start() chamado duas vezes… além de ser uma recursão desnecessária (o que vc tem contra while?)

Você pode “fazer funcionar” fazendo

t = new Thread();

No início do método start.

O ideal mesmo é que vc reestruture o seu código, até pq, o método estático ali também não tá muito legal.

Dica… Me parece que vc quer fazer uma tarefa que para e continua, certo?

O java tem classes de Timer prontas para isso, java.util.timer e java.swing.timer.

Eu também já desenvolvi uma, com a opção de pausar o timer e continuar de onde parou. A classe está aqui embaixo, para caso você queira estudar:

[code]/**

  • The <code>Chronometer</code> class is like a timer, but it can stop
  • counting time when desired and continue counting later.
  • Chronometer starts counting time by calling the <code>start()</code>
  • method. When the time limit is reached, the chronometer fires a
  • <code>TimeEvent</code> for all it’s listeners. If the user calls the
  • <code>stop()</code> method, the chronomether will freeze its counting. The
  • count sequence will proceed when the users call again the start
  • method. To restart counting, user may call <code>reset()</code> method at
  • anytime (even when the chronometer is running).
  • Chronometers can be ciclical or non-ciclical. A non-ciclical chronometer will
  • stop automatically when the timeLimit is exceeded. Then, it will reset itself
  • automatically. A ciclical chronometer will reset and counting again. So, it
  • will generate chronometer events until the user stops counting manually. The
  • precision parameter define the time, in miliseconds, that chronometer will
  • actualize itself. For example, in a chronomether with precision 50, the
  • chronometer will process each 50 miliseconds, so the current time will be 0,
  • 50, 100, 150 and so on. Smaller precision is usefull only when
  • <code>start()</code> and <code>stop()</code> will be used wihout calling
  • </code>reset()</code>. Smaller precision also demand more CPU resources.
  • The minimum precision of a chronometer is 5ms. The default chronometer
  • parameters are:
  • 30000 time limit;
  • 1000 precision;
  • 0 current time;
  • not ciclical;
  • @see ChronometerListener
  • @see TimeEvent
  • @author Mendonça, Vinícius Godoy de
    */
  • public class Chronometer {
    private volatile int currentTime = 0;
    private volatile int timeLimit = 30000;
    private volatile boolean isCiclic = false;
    private volatile int precision = 1000;

    private Engine engine = null;
    
    private String name = Chronometer.class.getName();
    private EventListenerList listenersList = new EventListenerList();
    
    public static final int MINIMUM_TIME_LIMIT = 10;
    public static final int MINIMUM_PRECISION = 5;
    
    /**
     * Notify all listeners that have registered interest for notification on
     * this undecoded message events. The event instance is lazily created using
     * the parameters passed into the fire method.
     */
    protected void fireTimeEvent() {
        TimeEvent evt = null;
        // Guaranteed to return a non-null array
        Object[] listeners = listenersList.getListenerList();
    
        // Process the listeners first to last, notifying
        // those that are interested in this event
        for (int i = 0; i &lt;= listeners.length - 2; i += 2)
            if (listeners[i] == ChronometerListener.class) {
                // Lazily create the event:
                if (evt == null)
                    evt = new TimeEvent(this);
                ((ChronometerListener) listeners[i + 1]).timeExceeded(evt);
            }
    }
    
    /**
     * Creates a new &lt;code&gt;Chronometer&lt;/code&gt;. <br>
     * The default chronometer parameters are:
     * <ul>
     * <li>30000 time limit;
     * <li>1000 precision;
     * <li>0 current time;
     * <li>not ciclical;
     * </ul>
     */
    public Chronometer() {
        this(Chronometer.class.getSimpleName());
    }
    
    /**
     * Creates a new &lt;code&gt;Chronometer&lt;/code&gt; with the given name. <br>
     * The default chronometer parameters are:
     * <ul>
     * <li>30000 time limit;
     * <li>1000 precision;
     * <li>0 current time;
     * <li>not ciclical;
     * </ul>
     */
    public Chronometer(String name) {
        super();
        currentTime = 0;
        setName(name);
    }
    
    /**
     * Creates a new &lt;code&gt;Chronometer&lt;/code&gt; with the given listener. <br>
     * The default chronometer parameters are:
     * <ul>
     * <li>30000 time limit;
     * <li>1000 precision;
     * <li>0 current time;
     * <li>not ciclical;
     * </ul>
     */
    public Chronometer(ChronometerListener listener) {
        this();
        addListener(listener);
    }
    
    /**
     * Creates a new &lt;code&gt;Chronometer&lt;/code&gt; with the given name and
     * listener. <br>
     * The default chronometer parameters are:
     * <ul>
     * <li>30000 time limit;
     * <li>1000 precision;
     * <li>0 current time;
     * <li>not ciclical;
     * </ul>
     */
    public Chronometer(String name, ChronometerListener listener) {
        this(name);
        addListener(listener);
    }
    
    /**
     * Sets the chronometer as ciclical or non-ciclical. A non-ciclical
     * chronometer will stop automatically when the time limit is exceeded.
     * Then, it will reset itself automatically. A ciclical chronometer will
     * reset and counting again. So, it will generate chronometer events until a
     * call to stop()
     * 
     * @param ciclic A boolean flag indicating if the chronometer is ciclical.
     * @see Chronometer#stop()
     */
    public void setCiclic(boolean ciclic) {
        this.isCiclic = ciclic;
    }
    
    /**
     * Indicate if the chronometer is ciclical or non-ciclical. A non-ciclical
     * chronometer will stop automatically when the time limit is exceeded.
     * Then, it will reset itself automatically. A ciclical chronometer will
     * reset and counting again. So, it will generate chronometer events until a
     * call to stop()
     * 
     * @see Chronometer#stop()
     * @see Chronometer#setCiclic(boolean)
     */
    public boolean isCiclic() {
        return isCiclic;
    }
    
    /**
     * Defines a new precision for this chronometer. The precision parameter
     * define the time, in miliseconds, that chronometer will actualize itself.
     * For example, in a chronomether with precision 50, the chronometer will
     * process each 50 miliseconds, so the current time will be 0, 50, 100, 150
     * and so on. Smaller precision is usefull only when &lt;code&gt;start()&lt;/code&gt;
     * and &lt;code&gt;stop()&lt;/code&gt; will be used wihout calling &lt;/code&gt;reset()&lt;/code&gt;.
     * Smaller precision also demand more CPU resources. The minimum precision
     * of a chronometer is 5ms.
     * 
     * @param miliseconds The new precision in miliseconds.
     */
    public void setPrecision(int miliseconds) throws IllegalArgumentException {
        if (miliseconds &lt; MINIMUM_PRECISION)
            throw new IllegalArgumentException(
                    &quot;Precision cannot be smaller than&quot; + MINIMUM_PRECISION
                            + &quot;.&quot;);
    
        precision = miliseconds;
    }
    
    /**
     * Retrieves the current precision. The precision parameter define the time,
     * in miliseconds, that chronometer will actualize itself. For example, in a
     * chronomether with precision 50, the chronometer will process each 50
     * miliseconds, so the current time will be 0, 50, 100, 150 and so on.
     * Smaller precision is usefull only when &lt;code&gt;start()&lt;/code&gt; and
     * &lt;code&gt;stop()&lt;/code&gt; will be used wihout calling &lt;/code&gt;reset()&lt;/code&gt;.
     * Smaller precision also demand more CPU resources. The minimum precision
     * of a chronometer is 5ms.
     */
    public int getPrecision() {
        return precision;
    }
    
    /**
     * Time that, when reached, make the chronometer fire its
     * &lt;code&gt;TimeEvent&lt;/code&gt;
     * 
     * @param miliseconds Time, in miliseconds.
     */
    public void setTimeLimit(int miliseconds) {
        if (miliseconds &lt; MINIMUM_TIME_LIMIT)
            throw new IllegalArgumentException(
                    &quot;Time Limit cannot be smaller than &quot; + MINIMUM_TIME_LIMIT
                            + &quot;.&quot;);
    
        timeLimit = miliseconds;
    }
    
    /**
     * Current elapsed time for this &lt;code&gt;Chronometer&lt;/code&gt;. Readings of
     * this parameter is directly afected by the chonometer precision.
     * 
     * @see Chronometer#getPrecision()
     */
    public int getCurrentTime() {
        return currentTime;
    }
    
    /**
     * Start counting the time.
     * 
     * @see Chronometer#stop()
     * @see Chronometer#reset()
     */
    public synchronized void start() {
        if (engine != null)
            return;
    
        engine = new Engine(getName());
        engine.start();
    }
    
    /**
     * Stop counting time. This action will not reset the
     * &lt;code&gt;Chronometer&lt;/code&gt;.
     * 
     * @see Chronometer#start()
     * @see Chronometer#reset()
     */
    public synchronized void stop() {
        if (engine == null)
            return;
    
        engine.interrupt();
        engine = null;
    }
    
    /**
     * Makes the chronometer restart it's time counting.
     * 
     * @see Chronometer#start()
     * @see Chronometer#stop()
     */
    public void reset() {
        currentTime = 0;
    }
    
    /**
     * Adds a listener for this &lt;code&gt;Chronometer&lt;/code&gt;.
     * 
     * @see ChronometerListener
     */
    public void addListener(ChronometerListener listener) {
        listenersList.add(ChronometerListener.class, listener);
    }
    
    /**
     * Removes the given listener from this &lt;code&gt;Chronometer&lt;/code&gt;.
     * 
     * @param listener The listener to remove.
     */
    public void removeListener(ChronometerListener listener) {
        listenersList.remove(ChronometerListener.class, listener);
    }
    
    /**
     * Returns this chronometer name.
     * 
     * @return The chronometer name.
     */
    public String getName() {
        return name;
    }
    
    /**
     * Sets this chronometer name. This same name will be used in the
     * chronometer thread every time the chronometer is running.
     * 
     * @param name The chronometer name.
     */
    public void setName(String name) {
        if (name == null || name.trim().equals(&quot;&quot;))
            throw new IllegalArgumentException(&quot;Name can't be null or blank!&quot;);
    
        this.name = name;
    }
    
    private class Engine extends Thread
    {
        public Engine(String name) {
            setDaemon(true);
            setName(name);
        }
    
        @Override
        public void run() {
            try {
                while (!isInterrupted()) {
                    currentTime += precision;
                    synchronized (this) {
                        wait(precision);
                    }
    
                    if (currentTime &gt;= timeLimit) {
                        currentTime = 0;
                        fireTimeEvent();
    
                        if (!isCiclic())
                            Chronometer.this.stop();
                    }
                }
            }
            catch (InterruptedException e) {
            }
        }
    }
    

    }[/code]

    Esse é o listener dessa classe (quase me esqueço).

    [code]
    /**

    • Listens for chronometer events.
      /
      public interface ChronometerListener extends EventListener {
      /
      *
      @throws Exception
      */
      public void timeExceeded(TimeEvent timeEvent);
      }[/code]
      O nome ficou infeliz pq originalmente era para ser uma classe de medição, mas acabou virando só um Timer espertinho mesmo. :slight_smile:

    Q legal kra, na verdade eu só estou usando Thread para poder aproveitar o sleep, se der pra tirar vai ser bom, está muito chato isso rsss
    Eu quero fazer exatamente isso q vc entendeu,é uma tarefa que pára e continua depois de uma hora.
    Vou estudar a sua classe e com certeza vou utilizá-la. Obrigado Vinnie!
    Eu deixei como a seguir, seguindo suas indicações assim mas o botão Start que chama o método go() (mudei o nome pra nao fazer confusão) fica travado:

    [code]package br.gov.infoseg.actions;

    import java.text.SimpleDateFormat;
    import java.util.Date;

    import br.gov.infoseg.gui.MainGui;

    /**

    • @author Felipe Rodrigues
      */

    public class TarefaContinua
    {
    /*
    1 segundo = 1.000 milisegundos
    1 minuto = 60 segundos = 60.000 milisegundos
    1 hora = 60 minutos = 3.600 segundos = 3.600.000 milisegundos
    */
    private static final long INTERVALO = 3600000;
    private static int CICLO = 1;
    private static int CONTADOR = 1;
    private static String[] s = new String[2];

    public static void stop() {
        //t = null;
    }	    		
    public static void exit() {				
    	System.exit(0);		
    }
    
    public void go()
    {		
    	try		
    	{	
    		ExportaConsulta ec = new ExportaConsulta();
    		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy - hh:mm:ss");			
    			
    		Thread t = new Thread();
    		t.start();		
    		
    		MainGui.jTextArea1.append("\nPROCESSO INICIADO...");
    		
    		Date d1 = new Date();			
    		MainGui.jTextArea1.append("\nINICIO: " + sdf.format(d1));						
    		System.out.println("INICIO: " + sdf.format(d1));
    							
    		//ec.verifyStatus(CICLO, CONTADOR); //Inicia todo o processo
    
    		Date d2 = new Date();
    		MainGui.jTextArea1.append("\nFIM: " + sdf.format(d2));			
    		System.out.println("FIM: " + sdf.format(d2) + "\n");
    		
    		CICLO++;
    		CONTADOR = 1;
    									
    		t.sleep(10000); //milisegundos 
    		go();
    	}
    	catch(Exception e)
    	{			
    		e.printStackTrace();
    	}
    }		
    

    }[/code]

    Ok… agora vamos aos outros problemas.

    Você dá um start numa thread sem um Runnable associado. A thread vai disparar e iniciar no método run() do Runnable. E seu código simplesmente não tem isso.

    Por isso, tudo que está no método go simplesmente continua rodando na thread do Swing.

    Sugestão: Dê um tempo no seu programa, pegue um livro de Java e estude bem o tópico threads. Desculpe se a frase pareceu rude, mas você ainda não está se batendo nos conceitos básicos.

    Sem problemas kra, tenho humildade suficiente pra reconhecer minhas falhas.
    Eu não tenho muita experiência com Threads, muito menos com Swing, então com certeza devo estar fazendo besteira.
    Ainda bem que possuo um livro muito bom, da própria Sun, de um curso que eu fiz e tem um capítulo inteiro sobre Threads, mas sobre Swing tem pouca coisa.
    Vou estudar isso até decorar…
    Obrigado pelos toques, e fique com Deus.

    [quote=ViniGodoy]Ok… agora vamos aos outros problemas.
    Você dá um start numa thread sem um Runnable associado. A thread vai disparar e iniciar no método run() do Runnable. E seu código simplesmente não tem isso.
    Por isso, tudo que está no método go simplesmente continua rodando na thread do Swing.
    Sugestão: Dê um tempo no seu programa, pegue um livro de Java e estude bem o tópico threads. Desculpe se a frase pareceu rude, mas você ainda não está se batendo nos conceitos básicos.[/quote]

    Kra, tirei a thread, e passei a usar Timer no lugar, me parece bem melhor e está funcionando:
    Classe Gui (trecho):

    public static void main(String args[]){
    		new MainGui();	
    	}
    		
    	private static ActionListener actionStart = new ActionListener(){
    		public void actionPerformed(ActionEvent actionEvent){
    			tc.IniciaProcesso();
    		}
    	};
    	
    	static Timer timer = new Timer(1000, actionStart);
    	
    	private static ActionListener actionExecute = new ActionListener(){
    		public void actionPerformed(ActionEvent actionEvent){			
    			jTextArea1.append("Processo iniciado...\n");
    			timer.start();
    		}
    	};
    	
    	private void initComponents() 
    	{	
    		setTitle("WebService Consulta Indivíduos");		
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    		jButton1 = new javax.swing.JButton();
    		jButton1.setText("Start");	
    		jButton1.addActionListener(actionExecute);
    ...

    Criei um ActionListener com a minha action propriamente dita, e outra ActionListener que chama essa primeira dentro de um Timer, aí chamo o timer.start ou stop quando é conveniente, e tem funcionado legal.
    Classe tarefa:

    public class TarefaContinua
    {	
    	private static int CICLO = 1;
    	private static int CONTADOR = 1;
    		
    	public void IniciaProcesso()
    	{		
    		try		
    		{
    			ExportaConsultax ec = new ExportaConsultax();
    			SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy - hh:mm:ss");						
    			
    			Date d1 = new Date();
    			MainGui.jTextArea1.append("Início: "+ sdf.format(d1) +"\n");								
    			
    			ec.VerificaIndividuos(CICLO, CONTADOR);			
    			
    			Date d2 = new Date();
    			MainGui.jTextArea1.append("Fim: "+ sdf.format(d2)+"\n");
    			MainGui.jTextArea1.append("Ciclo: "+ CICLO +"\n-\n");			
    			CICLO++;
    			CONTADOR = 1;			
    		}
    		catch(Exception e)
    		{			
    			e.printStackTrace();
    		}
    	}	
    }

    Porém continuo com o seguinte problema: quando faço apenas um append na jTextArea no método ec.VerificaIndividuos() pra ver se a chamada está chegando la, ele imprima na boa, mas quando mando realmente executar a tarefa ele fica travado, pois a tarefa é feita dentro de um loop.
    O que você acha disso?
    []´s

    O timer do Swing também roda na thread do swing. Na verdade, ele cria uma thread separada para disparar o actionListener, mas quando ele faz isso, transfere o processamento para a eventqueue do swing usando o EventQueue.invokeLater. Por isso você continua com problemas.

    Tente usar o Chronometer que postei ali em cima (é basicamente a mesma coisa) ou o java.util.timer.

    Esse post (e os outros 2 da página 2) talvez te ajude:
    http://www.guj.com.br/posts/list/52964.java#279083

    Ele mostra o uso de três técnicas (thread direto, java.util.timer e javax.swing.timer) para resolver o mesmo problema.

    Fala kra, bem esclarecedor o tópico, ainda mais com as suas respostas.
    Entendí que quando eu estava disparando o meu actionListener que possuía um Timer através do JButton, essa Action estava indo para a EventQueue, e pelo nome do método dela que você disse ser invocado, o invokeLater(), acredito que ela ficava em espera, causando a ‘trava’…
    Por isso decidi usar o java.util.Timer, ok.
    Agora fiquei na dúvida em como faço pra disparar a TimerTask pela ActionListener, afinal eu quero que isso se inicie quando eu clicar no JButton…

    Tentei colocar a chamada pra TimerTask dentro da ActionListener, mas está dando um erro:

    private static ActionListener ActionStart = new ActionListener(){
    		public void actionPerformed(ActionEvent actionEvent){			
    			Timer t = new Timer("ClockTimer", true);
    	        t.schedule(ProcessTask(), 0, 1000);
    		}
    	};
    	
    	private class ProcessTask extends TimerTask {
    		@Override
    	    public void run() {
    		    // Aqui chamamos o setHora através da EventQueue da AWT.
    		    // Conforme dito, isso garante Thread safety para o Swing.
    		    EventQueue.invokeLater(new Runnable() {
    		    	public void run() {
    		        // Só podemos chamar setHora diretamente dessa
    		        // forma, pois esse Runnable é uma InnerClass não
    		        // estática.
    		    		tc.IniciaProcesso();
    		        }
    		    });
    	    }
    	}

    Erro (linha 4 //t.schedule(ProcessTask(), 0, 1000); ):

    Qual seria a melhor maneira de associar uma TimerTask á uma ActionListener?

    Obrigado Vini.
    []´s

    Fiz a chamada, mas ainda assim não foi:

    class ProcessTask extends TimerTask {
    		@Override
    	    public void run() {
    		    // Aqui chamamos o metodo através da EventQueue da AWT.
    		    // Conforme dito, isso garante Thread safety para o Swing.
    		    EventQueue.invokeLater(new Runnable() {
    		    	public void run() {
    		    		tc.IniciaProcesso();
    		        }
    		    });
    	    }
    	}
    	
    	private void initComponents() throws InterruptedException
    	{	
    		setTitle("Consulta");		
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    		jButton1 = new javax.swing.JButton();
    		jButton1.setText("Start");	
    		jButton1.addActionListener(new ActionListener(){		    
    				public void actionPerformed(ActionEvent e){
    					t = new Timer("ClockTimer", true);
    					ProcessTask pt = new ProcessTask(); 
    					t.schedule(pt, 0, 1000);
    				}
    			}
    		);	 
    ...

    :roll:

    Você só deve chamar o invokeLater na hora de atualizar o Swing, no final.
    Senão o seu processamento paralelo vai deixar de ser.

    Não me lembro se tem como iniciar um timer direto com o java.util.timer. No final, acho que a solução com threads é a melhor mesmo:

    public void actionListener(ActionEvent evt) { new Thread(new ProcessTask()).start(); }

    E a classe ProcessTask fica assim:

    [code]class ProcessTask implements Runnable {
    @Override
    public void run() {
    //Faça o processamento da task aqui.

            // Aqui chamamos o metodo através da EventQueue da AWT.   
            // Conforme dito, isso garante Thread safety para o Swing.   
            EventQueue.invokeLater(new Runnable() {   
                public void run() {   
                     //Atualize o swing com os resultados aqui.
                }   
            });   
        }   
    }   [/code]

    [quote=ViniGodoy]Você só deve chamar o invokeLater na hora de atualizar o Swing, no final.
    Senão o seu processamento paralelo vai deixar de ser.[/quote]Ok, faz sentido.

    [quote=ViniGodoy]Não me lembro se tem como iniciar um timer direto com o java.util.timer. No final, acho que a solução com threads é a melhor mesmo:

    public void actionListener(ActionEvent evt) { new Thread(new ProcessTask()).start(); }[/quote]Ok, aqui vc associou a classe ProcessTask á essa nova Thread, que deixou de extender TimerTask para implementar Runnable…
    isso faz com que ela seja aplicável á uma Thread, é isso? Ou isso torna ela uma Thread propriamente dita?

    [quote=ViniGodoy]E a classe ProcessTask fica assim:

    class ProcessTask implements Runnable { @Override public void run() { //Faça o processamento da task aqui.[/quote]
    A chamada ao método que desejo rodar?

    [quote=ViniGodoy] // Aqui chamamos o metodo através da EventQueue da AWT. // Conforme dito, isso garante Thread safety para o Swing. EventQueue.invokeLater(new Runnable() { public void run() { //Atualize o swing com os resultados aqui. } }); } }[/quote]Neste caso a atualização do swing seria os appends que eu faço na JTextArea, para o operador poder acompanhar
    o que está acontecendo durante o processo…mas esses appends eu faço nos próprios métodos que executam a tarefa, invocando a classe do swing e
    fazendo appends na JTextArea…
    Bom, eu vou testar tudo isso aqui!!!
    Obrigado Vini.
    []´s

    O append não precisa ser executado no InvokeLater. Ele tem o seguinte comentário no seu Javadoc:
    This method is thread safe, although most Swing methods are not.

    Então, ali na parte do processamento, é só chamar o seu método mesmo. :slight_smile:

    O comando:

    Associa um Runnable a um objeto da classe Thread. Um objeto da classe Thread não é uma thread em si. Ele é simplesmente um objeto capaz de iniciar uma thread e monitora-la. A thread em si é um fluxo de execução alternativo do seu programa, que só vai iniciar quando start() for chamado. Esse novo fluxo inicia na primeira linha do método run, no caso, do objeto ProcessTask passado no construtor da classe Thread.

    É uma boa política separar a classe Thread (que é o mecanismo de execução) do Runnable, que é o código a ser executado propriamente dito.