Threads e Swing

9 respostas
J

Olá pessoal,
Estou quebrando a cabeça para fazer um exercicio que envolve threads e swing.
O contexto é o seguinte:
Eu tenho uma jpanel, na qual inseri labels (ao rodar coloco numeros), um botão 'calcular' e seis botões que simbolizam as threads.
Ao clicar em um botão 'calcular', no evento desse botão eu realizo cálculos utlizando seis threads, a partir de numeros inseridos na label.
Até aí tudo ok.
No entanto, eu tenho que atualizar os botões conforme o 'status' da thread.
Ou seja, devo mudar a cor:
-para branco quando ela estiver em execução,
- para amarelo quando ela estiver esperando outras threads executarem (join)
- para verde quando ela finalizar...
Coloquei delays nas threads (sleep) para poder visualizar as transições de estado.
Detalhe: nao posso usar observer, tem que ser de outra maneira (pode ser a machado, mas que funcione).

O que acontece, é que mudo a cor, mas eles não atualizam no jpanel. Qndo todas as threads terminam, ele somente atualiza para a cor verde (ultima).
Durante a execução a janela 'trava' e a mudança de cor não acontece. (nem repaint, nem numa outra thread usando SwingUtilities.invokeLater(new Thread())...

O que devo fazer para atualizar a cor do botão no jpanel, durante a execução das threads?

Alguém tem alguma idéia?

Valeu!

Abaixo os códigos:

private void botaoCalcularActionPerformed(java.awt.event.ActionEvent evt) {                                           

        btT1.setBackground(Color.GRAY);
        btT2.setBackground(Color.GRAY);
		//.... até aqui atualiza

        final int a = ((Number) txtA.getValue()).intValue();
        final int b = ((Number) txtB.getValue()).intValue();
		//...

        SwingUtilities.invokeLater(new Thread() {
            @Override
            public void run() {
                btT1.setBackground(Color.white); //não atualiza
                repaint();
                Thread1 t1 = new Tread1(b);
                t1.start();
                btT1.setBackground(Color.yellow); //não atualiza
                try {
                    t1.join();
                    btT1.setBackground(Color.green); // é a ultima cor setada.. que permanece
                } catch (InterruptedException ex) {
                    Logger.getLogger(TelaPrincipal.class.getName()).log(Level.SEVERE, null, ex);
                }
				// e repete o processo.. ao terminar todos, os botões somente ficam com a última cor setada (verde)

e na outra classe está assim

public class Tread1 extends Thread {
    private int resultadoT1;
    
    public Tread1 (int b) {
        resultadoT1 = b;
    }

    @Override
    public void run() {
        this.resultadoT1 = -resultadoT1;
        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            Logger.getLogger(T1.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public int getResultadoT1() {
        return resultadoT1;
    }
}

9 Respostas

V
  1. Você não está criando nenhuma thread. O InvokeLater dispara apenas eventos para a thread do Swing. No caso, tudo o que estiver no método run().
  2. Use o invokeLater apenas para atualizar componentes, dentro do método run() de sua thread.
  3. É o método start() que dispara uma thread.
G

e o t1 até q é uma thread realmente.

mas vc após chamar o start dela vc chama o join.
q faz a thread principal parar até q t1 finalize.

no final funcionou td como se fosse apenas na "thread main" mesmo.

creio q vc poderia tentar assim:

public abstract class Tread1 extends Thread {  
    private int resultadoT1;  
      
    public Tread1 (int b) {  
        resultadoT1 = b;  
    }  
  
    public abstract void Concluido();
    
    @Override  
    public void run() {  
        this.resultadoT1 = -resultadoT1;  
        try {  
            Thread.sleep(3000);
        } catch (InterruptedException ex) {  
            //Logger.getLogger(T1.class.getName()).log(Level.SEVERE, null, ex);  
        }  
        Concluido();
    }  
    public int getResultadoT1() {  
        return resultadoT1;  
    }  
}

note q adicionei um metodo abstrato "Concluido";//será executado qdo concluido

e use assim:
ps. note o override

SwingUtilities.invokeLater(new Thread() {  
            @Override  
            public void run() {  
                btT1.setBackground(Color.white); //não atualiza  
                repaint();  

                Tread1 t1 = new Tread1(b){			
        			@Override
        			public void Concluido() {
        				synchronized (btT1) {
                            btT1.setBackground(Color.green); // é a ultima cor setada.. que permanece  
        				}
        			}			
        		};  
                
                t1.start();  
                btT1.setBackground(Color.yellow); //não atualiza
J

Ambas dicas estão dando certo, pelas quais agradeço.
No entanto, notei que o problema surge quando uso o join().
Se usar o comando join() a tela principal (um jpanel, que está em uma jframe) não atualiza as cores até que tdas as treads finalizem.
Vou continuar tentando descobrir o que está errado.

G

josachu:
Ambas dicas estão dando certo, pelas quais agradeço.
No entanto, notei que o problema surge quando uso o join().
Se usar o comando join() a tela principal (um jpanel, que está em uma jframe) não atualiza as cores até que tdas as treads finalizem.
Vou continuar tentando descobrir o que está errado.

e o t1 até q é uma thread realmente.

mas vc após chamar o start dela vc chama o join.
q faz a thread principal parar até q t1 finalize.

no final funcionou td como se fosse apenas na “thread main” mesmo.

só repetindo!!!

G

se fizer do jeito q sujeri não precisaria do join

J

É verdade Gilson,

Mas um amigo sugeriu outra solução que funcionou. É a criação de uma nova thread, na qual incluo todo o código do botão.

É este código:

new Thread (new Runnable() {
            @Override
            public void run() {

            // todo o código do botão aqui dentro

           }
        }).start();

Pelo que entendi, o comando join() segura as thread anteriores, até que a thread corrente (a que dei o join) termine.
O que acontecia, é que eu segurava a thread do swing e portanto, ela só atualizava quando a thread que realizava a conta estava concluida.
Como eu queria atualizar o status mudando a cor do botão conforme o ‘status’ da thread, isso não acontecia, ou seja, travava e o botão ficava somente com a última alteração da cor (neste caso a verde).

M

Opa, tirou uma duvida minha tambem.
Fiz aqui e funcionou tudo certo.
VALEU

G

josachu:
É verdade Gilson,

Mas um amigo sugeriu outra solução que funcionou. É a criação de uma nova thread, na qual incluo todo o código do botão.

É este código:

new Thread (new Runnable() {
            @Override
            public void run() {

            // todo o código do botão aqui dentro

           }
        }).start();

Pelo que entendi, o comando join() segura as thread anteriores, até que a thread corrente (a que dei o join) termine.
O que acontecia, é que eu segurava a thread do swing e portanto, ela só atualizava quando a thread que realizava a conta estava concluida.
Como eu queria atualizar o status mudando a cor do botão conforme o ‘status’ da thread, isso não acontecia, ou seja, travava e o botão ficava somente com a última alteração da cor (neste caso a verde).

na realidade ele não seguras “as” threads anteriores e sim a thread q chamou o join.

no seu caso deu errado pq vc imaginou q a “thread” q chamava o join era uma distinta do thread main e não era, como bem explicou o ViniGodoy.
“InvokeLater dispara apenas eventos para a thread do Swing.” ou seja, faz com q o run seja executado posteriormente, porem pela thread principal.

é como um “enfileiramento” de trabalhos a serem feitos assim q o sistema estiver em idle.

pelo menos foi a leitura q consegui fazer. como não sou desenvolvedor java, posso estar enganado.

me corrijam se eu estiver errado, por gentileza.

J

GilsonNunes:
josachu:
É verdade Gilson,

Mas um amigo sugeriu outra solução que funcionou. É a criação de uma nova thread, na qual incluo todo o código do botão.

É este código:

new Thread (new Runnable() {
            @Override
            public void run() {

            // todo o código do botão aqui dentro

           }
        }).start();

Pelo que entendi, o comando join() segura as thread anteriores, até que a thread corrente (a que dei o join) termine.
O que acontecia, é que eu segurava a thread do swing e portanto, ela só atualizava quando a thread que realizava a conta estava concluida.
Como eu queria atualizar o status mudando a cor do botão conforme o ‘status’ da thread, isso não acontecia, ou seja, travava e o botão ficava somente com a última alteração da cor (neste caso a verde).

na realidade ele não seguras “as” threads anteriores e sim a thread q chamou o join.

no seu caso deu errado pq vc imaginou q a “thread” q chamava o join era uma distinta do thread main e não era, como bem explicou o ViniGodoy.
“InvokeLater dispara apenas eventos para a thread do Swing.” ou seja, faz com q o run seja executado posteriormente, porem pela thread principal.

é como um “enfileiramento” de trabalhos a serem feitos assim q o sistema estiver em idle.

pelo menos foi a leitura q consegui fazer. como não sou desenvolvedor java, posso estar enganado.

me corrijam se eu estiver errado, por gentileza.

Exatamente Gilson.
Quando executo a main, uso o método:

new TelaPrincipal().setVisible(true);

A main cria uma única thread chamada EventDispatchThread, que é thread é a principal (a thread do Swing).

Ela tem uma fila de processamento. Ao enviar o comando ela enfileira o comandos.

Se eu usar o “invokeLater” eu jogo o processo pra mesma fila (da thread principal).

Então uso um recurso da linguagem, que é a criação de uma classe anônima, e crio uma nova Thread.
Com isso, consigo ‘liberar’ o processamento da thread principal para a anônima.

Criado 25 de fevereiro de 2012
Ultima resposta 2 de mar. de 2012
Respostas 9
Participantes 4