CodeGym /Cursos /JAVA 25 SELF /Hilo EDT y operaciones largas en la UI

Hilo EDT y operaciones largas en la UI

JAVA 25 SELF
Nivel 50 , Lección 4
Disponible

1. ¿Qué es EDT (Event Dispatch Thread)?

En las aplicaciones gráficas en Java —ya sea Swing o JavaFX— todas las acciones del usuario (clics, teclas), así como el repintado de ventanas, se procesan en un hilo especial llamado EDT (Event Dispatch Thread, hilo de despacho de eventos).

¿Para qué sirve? Los componentes de la UI en Java no son seguros para hilos. Para evitar condiciones de carrera y artefactos, todos los cambios de la interfaz se realizan estrictamente en un único lugar: en el EDT. Es como una caja con un solo cajero: una misma compra no puede ser atendida por varias personas a la vez.

En Swing, el EDT ejecuta los manejadores de eventos y el repintado de componentes (por ejemplo, actionPerformed). En JavaFX el análogo es el JavaFX Application Thread, en el que se realizan las actualizaciones de la UI y manejadores como setOnAction.

2. El problema de las «operaciones largas» en la UI

¿Qué ocurre si en el EDT se ejecuta una operación larga?

Cuando el usuario pulsa un botón, el manejador (por ejemplo, actionPerformed o setOnAction) se ejecuta en el EDT. Si dentro lanzas una tarea pesada (lectura de un archivo grande, una petición de red, cálculos complejos), toda la UI se «congela»:

  • La ventana deja de responder a clics y teclas.
  • El repintado deja de funcionar — al moverla, la ventana se queda «congelada».
  • El usuario piensa que el programa se ha «roto».

Ejemplo incorrecto (Swing):

button.addActionListener(e -> {
    // ¡Operación larga directamente en el EDT!
    longOperation(); // Por ejemplo, lectura de un archivo grande
    label.setText("Listo!");
});

Resultado: mientras se ejecuta longOperation(), la ventana no responde al usuario.

¿Por qué? El EDT procesa las tareas en cola y solo puede ejecutar una a la vez. Mientras está ocupado con tu operación larga, no puede procesar ni los clics ni el repintado.

3. Solución: operaciones largas — solo en hilos en segundo plano

Principio:

  • Todas las operaciones largas — solo en hilos en segundo plano.
  • Todos los cambios de la UI — solo en el EDT/JavaFX Application Thread.

Lanzar una operación larga en un hilo separado

Ejemplo (Swing):

button.addActionListener(e -> {
    new Thread(() -> {
        longOperation(); // Se ejecuta en un hilo en segundo plano
        // Ahora hay que actualizar la UI, pero solo desde el EDT.
        SwingUtilities.invokeLater(() -> label.setText("Listo!"));
    }).start();
});

Ejemplo (JavaFX):

button.setOnAction(e -> {
    new Thread(() -> {
        longOperation();
        // Actualizamos la UI mediante Platform.runLater
        Platform.runLater(() -> label.setText("Listo!"));
    }).start();
});

¿Cómo actualizar la UI desde un hilo en segundo plano?

  • Swing: utiliza SwingUtilities.invokeLater(Runnable) — la tarea entrará en la cola del EDT.
  • JavaFX: utiliza Platform.runLater(Runnable) — la tarea se ejecutará en el JavaFX Application Thread.

¿Por qué no llamar label.setText(...) desde un hilo en segundo plano? Porque viola la seguridad de hilos de la UI: los componentes solo deben modificarse desde el hilo de la interfaz.

Clases especiales para tareas en segundo plano

En aplicaciones reales a menudo hay que mostrar progreso, permitir la cancelación y manejar errores. Para ello existen:

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

Ejemplo (JavaFX Task):

Task<Void> task = new Task<>() {
    @Override
    protected Void call() throws Exception {
        longOperation();
        // Se puede actualizar el progreso: updateProgress(...)
        return null;
    }
};

task.setOnSucceeded(e -> label.setText("Listo!"));
task.setOnFailed(e -> label.setText("Error"));

new Thread(task).start();

Ventajas: progreso, cancelación, eventos de éxito/errores. La modificación de la UI — mediante métodos seguros (updateMessage, updateProgress) o manejadores (setOnSucceeded y otros).

4. Patrones correctos e incorrectos

Incorrecto: operaciones largas en el manejador de eventos

button.setOnAction(e -> longOperation()); // ¡La UI se bloqueará!

Correcto: operaciones largas en un hilo separado

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

Aún mejor: 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("Listo!"));
    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("Listo!");
        }
    };
    worker.execute();
});

5. Práctica: ejemplo con la carga de un archivo

JavaFX:

button.setOnAction(e -> {
    Task<String> task = new Task<>() {
        @Override
        protected String call() throws Exception {
            // Simulación de una carga larga
            Thread.sleep(2000);
            return "Archivo cargado";
        }
    };
    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 "Archivo cargado";
        }
        @Override
        protected void done() {
            try {
                label.setText(get());
            } catch (Exception ex) {
                label.setText("Error");
            }
        }
    };
    worker.execute();
});

6. Errores típicos al trabajar con el EDT y operaciones largas

Error n.º 1: Operación larga en el EDT. Toda la aplicación se «congela», la ventana no responde, el usuario piensa que el programa se ha roto.

Error n.º 2: Intentar actualizar la UI desde un hilo en segundo plano. Violar la seguridad de hilos de la UI puede provocar bugs, artefactos y caídas. Utiliza SwingUtilities.invokeLater o Platform.runLater.

Error n.º 3: Ausencia de manejo de errores en la tarea en segundo plano. Las excepciones «se pierden», el usuario no sabe qué ha salido mal. En Swing — sobreescribe done() y lee get(); en JavaFX — suscríbete a setOnFailed.

Error n.º 4: No poder cancelar la operación larga. El usuario no puede interrumpir la carga o el cálculo. Utiliza el soporte de cancelación (SwingWorker.cancel, Task.cancel) y comprueba las banderas de cancelación dentro de la tarea.

Error n.º 5: Sin indicación de progreso. El usuario piensa que el programa «se ha colgado». En Swing — utiliza la publicación de resultados y una barra de progreso junto con SwingWorker; en JavaFX — updateProgress e indicadores visuales.

1
Cuestionario/control
Eventos y manejo de eventos, nivel 50, lección 4
No disponible
Eventos y manejo de eventos
Eventos y manejo de eventos
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION