1. Che cos’è l’EDT (Event Dispatch Thread)
Nelle applicazioni grafiche in Java — sia con Swing sia con JavaFX — tutte le azioni dell’utente (clic, tasti), così come il ridisegno delle finestre, sono gestite in un thread speciale chiamato EDT (Event Dispatch Thread, thread di gestione degli eventi).
A cosa serve? I componenti UI in Java non sono thread-safe. Per evitare race condition e artefatti, tutte le modifiche dell’interfaccia vengono eseguite rigorosamente in un unico posto — nell’EDT. È come una cassa con un solo cassiere: lo stesso scontrino non può essere elaborato contemporaneamente da più persone.
In Swing l’EDT avvia gli handler degli eventi e il ridisegno dei componenti (per esempio, actionPerformed). In JavaFX l’analogo è il JavaFX Application Thread, sul quale vengono eseguiti gli aggiornamenti dell’UI e gli handler come setOnAction.
2. Il problema delle “operazioni lunghe” nell'UI
Che cosa succede se nell’EDT avvii un’operazione lunga?
Quando l’utente preme un pulsante, l’handler (per esempio, actionPerformed o setOnAction) viene eseguito sull’EDT. Se al suo interno avvii un’attività pesante (lettura di un file grande, richiesta di rete, calcoli complessi), l’intera UI “si blocca”:
- La finestra smette di rispondere a clic e tasti.
- Il ridisegno si interrompe — spostando la finestra, l’immagine “rimane congelata”.
- L’utente pensa che il programma si sia “bloccato”.
Esempio di codice errato (Swing):
button.addActionListener(e -> {
// Operazione lunga direttamente nell'EDT!
longOperation(); // Per esempio, lettura di un file grande
label.setText("Fatto!");
});
Risultato: mentre longOperation() è in esecuzione, la finestra non risponde all’utente.
Perché? L’EDT elabora i task in coda e può eseguirne solo uno alla volta. Finché è impegnato con la tua operazione lunga, non può gestire né i clic né il ridisegno.
3. Soluzione: operazioni lunghe — solo nei thread in background
Principio:
- Tutte le operazioni lunghe — solo nei thread di background.
- Tutte le modifiche all’UI — solo su EDT/JavaFX Application Thread.
Avviare un’operazione lunga in un thread separato
Esempio (Swing):
button.addActionListener(e -> {
new Thread(() -> {
longOperation(); // Eseguita in un thread di background
// Ora bisogna aggiornare l'UI — ma solo dall'EDT!
SwingUtilities.invokeLater(() -> label.setText("Fatto!"));
}).start();
});
Esempio (JavaFX):
button.setOnAction(e -> {
new Thread(() -> {
longOperation();
// Aggiorniamo l'UI tramite Platform.runLater
Platform.runLater(() -> label.setText("Fatto!"));
}).start();
});
Come aggiornare l’UI da un thread di background?
- Swing: usare SwingUtilities.invokeLater(Runnable) — il task finirà in coda all’EDT.
- JavaFX: usare Platform.runLater(Runnable) — il task verrà eseguito nel JavaFX Application Thread.
Perché non si può semplicemente chiamare label.setText(...) da un thread di background? Perché viola la thread-safety dell’UI: i componenti devono essere modificati solo dal thread dell’interfaccia.
Classi speciali per i task in background
Nelle applicazioni reali spesso bisogna mostrare l’avanzamento, consentire l’annullamento e gestire gli errori. Per questo esistono:
- SwingWorker<T, V> — per Swing;
- Task<V>, Service<V> — per JavaFX.
Esempio (JavaFX Task):
Task<Void> task = new Task<>() {
@Override
protected Void call() throws Exception {
longOperation();
// È possibile aggiornare l'avanzamento: updateProgress(...)
return null;
}
};
task.setOnSucceeded(e -> label.setText("Fatto!"));
task.setOnFailed(e -> label.setText("Errore!"));
new Thread(task).start();
Vantaggi: avanzamento, annullamento, eventi di successo/errore. La modifica dell’UI — tramite metodi sicuri (updateMessage, updateProgress) o handler (setOnSucceeded ecc.).
4. Pattern corretti e scorretti
Sbagliato: operazioni lunghe nell’handler degli eventi
button.setOnAction(e -> longOperation()); // l'UI si bloccherà!
Giusto: operazioni lunghe in un thread separato
button.setOnAction(e -> new Thread(() -> longOperation()).start());
Ancora meglio: usare 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("Fatto!"));
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("Fatto!");
}
};
worker.execute();
});
5. Pratica: esempio con il caricamento di un file
JavaFX:
button.setOnAction(e -> {
Task<String> task = new Task<>() {
@Override
protected String call() throws Exception {
// Simulazione di un caricamento lungo
Thread.sleep(2000);
return "File caricato!";
}
};
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 "File caricato!";
}
@Override
protected void done() {
try {
label.setText(get());
} catch (Exception ex) {
label.setText("Errore!");
}
}
};
worker.execute();
});
6. Errori tipici lavorando con l’EDT e operazioni lunghe
Errore n. 1: Operazione lunga nell’EDT. L’intera applicazione “si blocca”, la finestra non risponde, l’utente pensa che il programma si sia bloccato.
Errore n. 2: Tentativo di aggiornare l’UI da un thread di background. La violazione della thread-safety dell’UI può portare a bug, artefatti e crash. Usare SwingUtilities.invokeLater o Platform.runLater.
Errore n. 3: Mancata gestione degli errori nel task in background. Le eccezioni “si perdono”, l’utente non sa cosa sia andato storto. In Swing — sovrascrivere done() e leggere get(); in JavaFX — registrarsi a setOnFailed.
Errore n. 4: Nessuna possibilità di annullare l’operazione lunga. L’utente non può interrompere il caricamento/calcolo. Usare il supporto per l’annullamento (SwingWorker.cancel, Task.cancel) e controllare i flag di annullamento all’interno del task.
Errore n. 5: Nessuna indicazione di avanzamento. L’utente pensa che il programma si sia “bloccato”. In Swing — usare la pubblicazione dei risultati e una progress bar insieme a SwingWorker; in JavaFX — updateProgress e indicatori visivi.
GO TO FULL VERSION