JButton + Thread

28 respostas
R

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!

28 Respostas

T

Tenta da um t.stop();

V

[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.

V

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.

R

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!

V
  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

V

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.

R

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.

V

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.

V

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:

/**
 * The <code>Chronometer</code> class is like a timer, but it can stop
 * counting time when desired and continue counting later. <br>
 * Chronometer starts counting time by calling the &lt;code&gt;start()&lt;/code&gt;
 * method. When the time limit is reached, the chronometer fires a
 * &lt;code&gt;TimeEvent&lt;/code&gt; for all it's listeners. If the user calls the
 * &lt;code&gt;stop()&lt;/code&gt; method, the chronomether will freeze its counting. The
 * count sequence will <i>proceed</i> when the users call again the start
 * method. To restart counting, user may call &lt;code&gt;reset()&lt;/code&gt; method at
 * anytime (even when the chronometer is running).
 * <p>
 * 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
 * &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. The default chronometer
 * parameters are:
 * <ul>
 * <li>30000 time limit;
 * <li>1000 precision;
 * <li>0 current time;
 * <li>not ciclical;
 * </ul>
 * <p>
 * 
 * @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) {
            }
        }
    }
}

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

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

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:

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();
		}
	}		
}
V

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.

R

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.

R

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.

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

V

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.

R

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

R

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:

V

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:

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.
                }   
            });   
        }   
    }
R

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.
Ok, faz sentido.

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(); }

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?

ViniGodoy:
E a classe ProcessTask fica assim:

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


A chamada ao método que desejo rodar?

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. } }); } }
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

V

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.

R

Então neste caso eu voltaria a usar o método sleep() da classe Thread, já que não usa mais a Timertask…

V

Isso aí roda o que tem no Runnable, uma única vez, daí a thread morre…

E sim, se vc quiser dar uma pausinha, é só usar o Thread.sleep. :slight_smile:

R
class ProcessTask implements Runnable {   
        @Override   
        public void run() {   		
            //Aqui chamamos o metodo através da EventQueue da AWT.   
            //Conforme dito, isso garante Thread safety para o Swing.
        	tc.IniciaProcesso();
            EventQueue.invokeLater(new Runnable() {   
                public void run() {   
                     //Atualize o swing com os resultados aqui.
                }   
            });   
        }   
    }
Tá dando erro na linha da declaração do método run da ProcessTask (quote abaixo), na linha 3, se eu comento o '@Override' aí ele para de dar erro, mas não faz sentido, o método a ser sobrescrito não é justamente o run()?
The method run() of type MainGui.ProcessTask must override a superclass method
Segue abaixo a sua sugestão, Vini:
private void initComponents() throws InterruptedException
	{	
		setTitle("WebService Consulta");		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		jButton1 = new javax.swing.JButton();
		jButton1.setText("Start");	
		jButton1.addActionListener(new ActionListener(){		    
				public void actionPerformed(ActionEvent e){
					new Thread(new ProcessTask()).start();										
				}
			}
		);
Como faço pra dar o sleep na Thread, quando ela é declarada desta maneira com 'new'? Tentei da seguinte maneira, mas nao funcionou... :/
private void initComponents() throws InterruptedException
	{	
		setTitle("WebService Consulta");		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		jButton1 = new javax.swing.JButton();
		jButton1.setText("Start");	
		jButton1.addActionListener(new ActionListener(){		    
				public void actionPerformed(ActionEvent e){
					Thread t = new Thread(new ProcessTask()).start();										
                                         t.sleep(3600000);
				}
			}
		);
A Thread em questão vai saber que só é para ela dormir durante o tempo especificado depois que a tarefa no método chamado em Runnable for concluída? Ou vai dormir logo depois de realizar a chamada? Declarar a Thread desta maneira está dando o seguinte erro:
Type mismatch: cannot convert from void to Thread...
Creio q assim não seja a melhor maneira...rs...mais uma vez obrigado.
V

O sleep faz a thread atual dormir.

Quem é a thread atual?

No método actionPerformed é a thread do Swing.
E no método run() é a nova thread, que você acabou de criar com o new.

Para fazer sua segunda thread dormir (e não a do botão), você deve mover o sleep para dentro do run, ou de qualquer método chamado pelo run.

R
Blz kra, it worked... 8)
class ProcessTask implements Runnable {   
        //@Override   
        public void run() {   		
            //Aqui chamamos o metodo através da EventQueue da AWT.   
            //Conforme dito, isso garante Thread safety para o Swing.
        	tc.IniciaProcesso();
            /*EventQueue.invokeLater(new Runnable() {   
                public void run() {   
                     //Atualize o swing com os resultados aqui.
                }   
            });   */
        }   
    }   

	private void initComponents() throws InterruptedException
	{	
		setTitle("WebService Consulta Indivíduos");		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		jButton1 = new javax.swing.JButton();
		jButton1.setText("Start");	
		jButton1.addActionListener(new ActionListener(){		    
				public void actionPerformed(ActionEvent e){
					new Thread(new ProcessTask()).start();
				}
			}
		);	 
...
Reparou q eu comentei a EventQueue.invokeLater? E ainda assim funcionou... O que exatamente eu teria necessidade de 'invocar tardiamente' ali?E o sleep eu coloquei dentro do método da outra classe:
public void IniciaProcesso()
	{		
		try		
		{
			ExportaConsultax ecx = new ExportaConsultax();
			SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy - hh:mm:ss");	
			SimpleDateFormat sdfLog = new SimpleDateFormat("dd_MM_yyyy-hh.mm.ss");
			
			Thread t = new Thread();
			t.start();			
				
				ecx.VerificaIndividuos(CICLO, CONTADOR);
				
				Date d3 = new Date();				
				fc.createFile("C:\\", "ClienteInterfaceIndividuo_"+ sdfLog.format(d3) +".log", log.getLog());
				log.setLog(null);
				
				CICLO++;
				CONTADOR = 1;	
			
			t.sleep(INTERVALO); //milisegundos			
			IniciaProcesso();
		}
		catch(Exception e)
		{			
			e.printStackTrace();
		}
	}

Voltei um pouco á lógica inicial...o problema agora é q a JTextArea não está descendo com o scroll conforme vai acrescentando texto, mas aí já é outra história!!!
Preciso também criar um método para pausar esta thread, vou olhar nakele seu outro tópico.
VALEU Vini!

V

Você precisa chamar o invokeLater sempre que for atualizar um componente do Swing que não seja thread-safe. Não é o caso do JTextArea, mas é o caso da maioria: do JButton, JLabel, JTextField, e vários outros.

Geralmente, você só coloca os sets desses componentes dentro do InvokeLater, raramente um processamento.

Para movimentar o cursor do JTextArea é uma xunxeira desgraçada, por conta de uns bugs do próprio JTextPane.

Há uns tempos atrás postei aqui o código de um PrintWriter que escreve num JTextArea ou JTextPane, e já faz esse lance do cursor. Talvez você possa usa-lo. Ele também é thread-safe. O bom é que daí vc pode escrever no JTextArea com todos os métodos que vc teria usando o System.out, inclusive o printf. :slight_smile:

Aqui está o link:
http://www.guj.com.br/posts/list/83462.java#445238

Eu geralmente uso com JTextPane para poder definir cores e formatação. Desenvolvemos um componente aqui baseado no Antlr que faz colorização automática dos nossos logs. :smiley:

J

Brother não li as respostas pois estou com pressa, so li seu primeiro post…

Quanto a ficar travado, isso acontece pq o Swing tem prioridade diferentes em relação a seus componentes, por isso seu botão fica travado enquanto um Thread que foi chamada a partir da interface gráfica está rodando!
Faça o seguinte:

Thread threadProgress = new Thread(new Runnable() {
				public void run() {
					Controlador.diGrafu.getJProgressBar().setString("Executando...");
					Controlador.diGrafu.getJProgressBar().setIndeterminate(true);
				}
			});
			threadProgress.setDaemon(true);
			threadProgress.start();

			Thread threadDiGrafu = new Thread(new Runnable() {
				public void run() {
					ControlaExecucao.executaDiGrafu();
					Controlador.diGrafu.getJProgressBar().setVisible(false);
				}
			});
			threadDiGrafu .start();

Essas 2 Threads (threadProgress e threadDiGrafu ) uma controla um progressBar e a outra algumas execuções que eu faço.

Vc pode controlar seus botões na Thread threadProgress e as outras execuções que vc quer fazer na Thread threadDiGrafu

Se o seu problema é o que eu estou pensando com certeza vai dar certo…

flw

R

Foi mal people, andei enrolado com outras coisas (do mesmo sistema) e a interface acabou ficando por último...(prioridade é prioridade)
Mas não tem problema, o pesadelo voltou! rs...
Bom, como eu postei acima, consegui fazer a chamada do método que possui uma thread controladora de intervalos pelo JButton.
Eu clico no JButton e ele chama o método, esse método por sua vez inicia uma thread e esta thread controla intervalos de tempo neste método.

Agora criei um botão para pausar o funcionamento da thread, e adivinha: ele está ficando travado :roll:

Vini Godoy:
Você precisa chamar o invokeLater sempre que for atualizar um componente do Swing que não seja thread-safe. Não é o caso do JTextArea, mas é o caso da maioria: do JButton, JLabel, JTextField, e vários outros. Geralmente, você só coloca os sets desses componentes dentro do InvokeLater, raramente um processamento. Para movimentar o cursor do JTextArea é uma xunxeira desgraçada, por conta de uns bugs do próprio JTextPane.

Creio que eu tenha que usar o InvokeLater neste JButton que invoca o Pause, concorda?
Eu fiz, mas nao adiantou...Vini, você disse atualizar...porém, creio que nao seria bem atualizar o JButton neste caso.

Thread t = new Thread();

public void Pause() throws InterruptedException{
	log.setPAUSE(true);
	t.wait();
}
	
public void Resume() throws InterruptedException{
	log.setPAUSE(false);		
	t.notify();
}	
	
public void IniciaProcesso()
{		
	try		
	{		
		t.start();
             ecx.VerificaIndividuos(CICLO, CONTADOR);
        t.sleep(INTERVALO); //milisegundos
		IniciaProcesso();
	}
	catch(Exception e)
	{			
		e.printStackTrace();
	}
}

Objetivo: se a pessoa clica no JButton 'Start' e deixa, o sistema executa a tarefa e depois de uma hora executa novamente, e assim fica eternamente.
Se alguém clicar no JButton 'Pause', ela tem que ficar parada até clicarem no Start novamente.
Acontece que quando invoco o método Pause pelo JButton ele trava o botão Pause, fica travado e a thread não pausa. :roll:

R

Resolvi colocar a Thread dentro da classe que usa o Swing, pois ficou mais simples e organizado.
(trecho):

private Thread t = new Thread();
public MainGui() {
        initComponents();
    }   
            
    class ProcessTask implements Runnable {   
        //@Override   
        public void run() {   		
            //Aqui chamamos o metodo através da EventQueue da AWT.   
            //Conforme dito, isso garante Thread safety para o Swing.
        	new TarefaContinuax();
            /*EventQueue.invokeLater(new Runnable() {   
                public void run() {   
                     //Atualize o swing com os resultados aqui.
                }   
            });   */
        }   
    }   
	
	public ActionListener ActionStart = new ActionListener() {		    
		public void actionPerformed(ActionEvent e) {
			new Thread(new ProcessTask()).start();				
		}
	};	
	
	ActionListener ActionPause = new ActionListener() {		    
		public void actionPerformed(ActionEvent e) {           	
			jTextArea1.append("\nProcesso interrompido...");           	
           	try{
           		t.wait();
           	}catch(InterruptedException ie){
           		jTextArea1.append("\nErro: "+ie.getMessage());
           	}
        }           							
	};
	
	ActionListener ActionResume = new ActionListener() {		    
		public void actionPerformed(ActionEvent e) {           	
			jTextArea1.append("\nProcesso interrompido...");
      		t.notify();           	
        }           							
	};

A dúvida agora é como controlar a chamada de new TarefaContinua(); para acontecer de uma em uma hora, e os métodos para pausar e resumir a thread…
Agradeço qualquer ajuda pessoal.
[]´s

Criado 10 de março de 2008
Ultima resposta 25 de mar. de 2008
Respostas 28
Participantes 4