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.
GO TO FULL VERSION