CodeGym /Corsi /JAVA 25 SELF /Thread EDT e operazioni lunghe nell'UI

Thread EDT e operazioni lunghe nell'UI

JAVA 25 SELF
Livello 50 , Lezione 4
Disponibile

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.

1
Sondaggio/quiz
Eventi e gestione degli eventi, livello 50, lezione 4
Non disponibile
Eventi e gestione degli eventi
Eventi e gestione degli eventi
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION