Swing y JFC: guía completa para interfaces gráficas en Java 21

SwingWorker<T, V> es la solución oficial de Swing para ejecutar trabajo pesado fuera del EDT sin congelar la interfaz. El tipo T es el resultado final y V es el tipo de los datos intermedios publicados durante el progreso.

import javax.swing.*;
import java.util.List;

public class CargaWorker extends SwingWorker<String, Integer> {

    private final JProgressBar barra;
    private final JLabel estado;

    public CargaWorker(JProgressBar barra, JLabel estado) {
        this.barra  = barra;
        this.estado = estado;
    }

    @Override
    protected String doInBackground() throws Exception {
        // Se ejecuta en un hilo de fondo (no en el EDT)
        for (int i = 0; i <= 100; i += 10) {
            Thread.sleep(200); // simula trabajo
            publish(i);        // envía progreso al EDT
            setProgress(i);
        }
        return "Carga completada";
    }

    @Override
    protected void process(List<Integer> chunks) {
        // Se ejecuta en el EDT con los valores publicados
        int ultimo = chunks.getLast(); // Java 21: getLast() en List
        barra.setValue(ultimo);
    }

    @Override
    protected void done() {
        // Se ejecuta en el EDT cuando doInBackground() termina
        try {
            estado.setText(get()); // obtiene el valor de retorno
        } catch (Exception ex) {
            estado.setText("Error: " + ex.getMessage());
        }
    }
}

Usar SwingWorker

JProgressBar barra = new JProgressBar(0, 100);
JLabel estado = new JLabel("Listo");
JButton boton = new JButton("Iniciar");

boton.addActionListener(e -> {
    boton.setEnabled(false);
    CargaWorker worker = new CargaWorker(barra, estado);
    worker.addPropertyChangeListener(evt -> {
        if ("state".equals(evt.getPropertyName()) &&
            SwingWorker.StateValue.DONE == evt.getNewValue()) {
            boton.setEnabled(true);
        }
    });
    worker.execute();
});

Timer: acciones periódicas en el EDT

javax.swing.Timer ejecuta una acción repetidamente en el EDT, ideal para animaciones, reloj en pantalla o actualización periódica de datos:

JLabel reloj = new JLabel();

Timer timer = new Timer(1000, e -> {
    reloj.setText(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
});
timer.setInitialDelay(0);
timer.start();

// Detener cuando se cierre la ventana
frame.addWindowListener(new WindowAdapter() {
    @Override public void windowClosing(WindowEvent e) { timer.stop(); }
});

Virtual Threads y Swing (Java 21)

Java 21 introduce los Virtual Threads (Thread.ofVirtual()), que son ligeros y no bloquean el hilo del OS. Aunque no reemplazan a SwingWorker para actualizaciones de UI, simplifican el lanzamiento de tareas en segundo plano:

boton.addActionListener(e -> {
    Thread.ofVirtual().start(() -> {
        String resultado = servicioLento.obtenerDatos(); // en virtual thread
        SwingUtilities.invokeLater(() -> campo.setText(resultado)); // vuelta al EDT
    });
});

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP