CodeGym /Cursos /JAVA 25 SELF /Thread EDT e operações longas na UI

Thread EDT e operações longas na UI

JAVA 25 SELF
Nível 50 , Lição 4
Disponível

1. O que é EDT (Event Dispatch Thread)

Em aplicativos gráficos em Java — seja Swing ou JavaFX — todas as ações do usuário (cliques, teclas), bem como a repintura das janelas, são processadas em uma thread especial chamada EDT (Event Dispatch Thread, thread de despacho de eventos).

Por que ela é necessária? Os componentes de UI em Java não são thread-safe. Para evitar condições de corrida e artefatos, todas as mudanças na interface são executadas estritamente em um único lugar — na EDT. É como um caixa com um único atendente: o mesmo recibo não pode ser manipulado por várias pessoas ao mesmo tempo.

No Swing, a EDT aciona os manipuladores de eventos e a repintura dos componentes (por exemplo, actionPerformed). No JavaFX, o análogo é o JavaFX Application Thread, no qual são executadas as atualizações da UI e handlers como setOnAction.

2. O problema das “operações longas” na UI

O que acontece se você executar uma operação demorada na EDT?

Quando o usuário clica em um botão, o handler (por exemplo, actionPerformed ou setOnAction) é executado na EDT. Se dentro dele você iniciar uma tarefa pesada (leitura de um arquivo grande, requisição de rede, cálculos complexos), toda a UI “trava”:

  • A janela deixa de responder a cliques e teclas.
  • A repintura para de funcionar — ao mover, a janela fica “travada”.
  • O usuário acha que o programa “parou de funcionar”.

Exemplo de código incorreto (Swing):

button.addActionListener(e -> {
    // Operação demorada diretamente na EDT!
    longOperation(); // Por exemplo, leitura de um arquivo grande
    label.setText("Concluído!");
});

Resultado: enquanto longOperation() estiver em execução, a janela não responde ao usuário.

Por quê? A EDT processa as tarefas em fila e só pode executar uma por vez. Enquanto estiver ocupada com sua operação demorada, ela não pode processar cliques nem repintura.

3. Solução: operações longas — somente em threads em segundo plano

Princípio:

  • Todas as operações demoradas — somente em threads de segundo plano.
  • Todas as mudanças na UI — somente na EDT/JavaFX Application Thread.

Iniciar uma operação demorada em uma thread separada

Exemplo (Swing):

button.addActionListener(e -> {
    new Thread(() -> {
        longOperation(); // Executa em thread de segundo plano
        // Agora precisamos atualizar a UI — mas apenas a partir da EDT!
        SwingUtilities.invokeLater(() -> label.setText("Concluído!"));
    }).start();
});

Exemplo (JavaFX):

button.setOnAction(e -> {
    new Thread(() -> {
        longOperation();
        // Atualizamos a UI via Platform.runLater
        Platform.runLater(() -> label.setText("Concluído!"));
    }).start();
});

Como atualizar a UI a partir de uma thread em segundo plano?

  • Swing: use SwingUtilities.invokeLater(Runnable) — a tarefa irá para a fila da EDT.
  • JavaFX: use Platform.runLater(Runnable) — a tarefa será executada no JavaFX Application Thread.

Por que não podemos simplesmente chamar label.setText(...) de uma thread em segundo plano? Porque isso viola a segurança de threads da UI: os componentes devem ser alterados somente a partir da thread da interface.

Classes especiais para tarefas em segundo plano

Em aplicativos reais, muitas vezes é preciso mostrar progresso, permitir cancelamento e tratar erros. Para isso, existem:

  • SwingWorker<T, V> — para Swing;
  • Task<V>, Service<V> — para JavaFX.

Exemplo (JavaFX Task):

Task<Void> task = new Task<>() {
    @Override
    protected Void call() throws Exception {
        longOperation();
        // É possível atualizar o progresso: updateProgress(...)
        return null;
    }
};

task.setOnSucceeded(e -> label.setText("Concluído!"));
task.setOnFailed(e -> label.setText("Erro!"));

new Thread(task).start();

Vantagens: progresso, cancelamento, eventos de sucesso/erro. Alterações na UI — por meio de métodos seguros (updateMessage, updateProgress) ou handlers (setOnSucceeded e outros).

4. Padrões corretos e incorretos

Incorreto: operações longas no handler de eventos

button.setOnAction(e -> longOperation()); // A UI ficará travada!

Correto: operações longas em uma thread separada

button.setOnAction(e -> new Thread(() -> longOperation()).start());

Melhor ainda: usar Task/Worker

JavaFX:

button.setOnAction(e -> {
    Task<Void> task = new Task<>() {
        @Override
        protected Void call() throws Exception {
            longOperation();
            return null;
        }
    };
    task.setOnSucceeded(ev -> label.setText("Concluído!"));
    new Thread(task).start();
});

Swing:

button.addActionListener(e -> {
    SwingWorker<Void, Void> worker = new SwingWorker<>() {
        @Override
        protected Void doInBackground() throws Exception {
            longOperation();
            return null;
        }
        @Override
        protected void done() {
            label.setText("Concluído!");
        }
    };
    worker.execute();
});

5. Prática: exemplo de carregamento de arquivo

JavaFX:

button.setOnAction(e -> {
    Task<String> task = new Task<>() {
        @Override
        protected String call() throws Exception {
            // Simulação de carregamento demorado
            Thread.sleep(2000);
            return "Arquivo carregado!";
        }
    };
    task.setOnSucceeded(ev -> label.setText(task.getValue()));
    new Thread(task).start();
});

Swing:

button.addActionListener(e -> {
    SwingWorker<String, Void> worker = new SwingWorker<>() {
        @Override
        protected String doInBackground() throws Exception {
            Thread.sleep(2000);
            return "Arquivo carregado!";
        }
        @Override
        protected void done() {
            try {
                label.setText(get());
            } catch (Exception ex) {
                label.setText("Erro!");
            }
        }
    };
    worker.execute();
});

6. Erros típicos ao trabalhar com a EDT e operações longas

Erro nº 1: Operação demorada na EDT. Todo o aplicativo “trava”, a janela não responde, o usuário pensa que o programa parou de funcionar.

Erro nº 2: Tentar atualizar a UI de uma thread em segundo plano. A violação da segurança de threads da UI pode levar a bugs, artefatos e quedas. Use SwingUtilities.invokeLater ou Platform.runLater.

Erro nº 3: Ausência de tratamento de erros na tarefa em segundo plano. Exceções “se perdem”, o usuário não sabe o que deu errado. No Swing — sobrescreva done() e leia get(); no JavaFX — assine setOnFailed.

Erro nº 4: Não há como cancelar a operação demorada. O usuário não consegue interromper o carregamento/cálculo. Use suporte a cancelamento (SwingWorker.cancel, Task.cancel) e verifique os sinais de cancelamento dentro da tarefa.

Erro nº 5: Sem indicação de progresso. O usuário acha que o programa “travou”. No Swing — use publicação de resultados e uma barra de progresso junto com SwingWorker; no JavaFX — updateProgress e indicadores visuais.

1
Pesquisa/teste
Eventos e processamento de eventos, nível 50, lição 4
Indisponível
Eventos e processamento de eventos
Eventos e processamento de eventos
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION