thread example java multithreading user-interface swing freeze

java - example - Mi GUI está congelada



java worker (5)

Debe leer Concurrency in Swing para entender cómo operan EDT y SwingWorkers.

Todas las actualizaciones de la GUI se ejecutan en el EDT, de modo que al hacer clic en un componente de la GUI, cualquier método que invoque se ejecutará en el EDT. Si se trata de un proceso lento, esto impedirá que el EDT ejecute actualizaciones futuras de la GUI. Por lo tanto, su GUI se está congelando y no puede hacer clic en el botón de pausa.

Necesita usar SwingWorker para ejecutar el proceso lento en otro hilo. El enlace que proporcioné arriba detalla cómo hacer esto.

Tengo algo que no puedo entender: mi GUI Swing contiene un botón "reproducir" y "pausa". También tengo una variable estática que define los estados ''ON'' y ''OFF''. (El programa principal genera la GUI). Al hacer clic en ''jugar'' cambio el estado de mi variable estática a ''ACTIVADO'' y lanzo un proceso que consume mucho tiempo en un hilo que también modifica la GUI. Siempre que la variable estática esté ''ON'' en el mismo proceso. Al hacer clic en ''pausa'' cambiaría la variable estática a OFF. Pero al hacer clic en "reproducir" la GUI se congela y, en consecuencia:

  1. La GUI no se actualiza
  2. El proceso no puede ''pausarse'' con mi botón ''pausa''.

He oído hablar de EDT y SwingWorker pero tengo una forma simple de hacerlo. Lo tomo.

Gracias por tu ayuda y perdona mi mal inglés ...


El hilo de envío de eventos (EDT) es el único hilo en el que es seguro leer o actualizar la GUI.

El botón de pausa debe establecer la variable de encendido / apagado en el hilo de envío del evento.

La operación que consume mucho tiempo, y el ciclo, no deberían estar en el EDT. (El bucle tampoco debe ejecutarse continuamente sin hacer nada más que verificar la variable, o puede consumir fácilmente toda su CPU. Si no tiene nada más que hacer, debe verificar y luego llamar a Thread.sleep() durante un período de tiempo ( decir 100 ms).)

Si puede probar que la variable de activación / desactivación se configura en DESACTIVADA, pero que, sin embargo, siempre se lee como ACTIVADA, es posible que el valor de la variable no se copie desde el EDT al hilo de trabajo. Haz que sea volatile , o synchronize acceso a él, o utiliza una AtomicReference , o léelo en el EDT usando SwingUtilities.invokeAndWait() .

SwingWorker es probablemente la forma más sencilla de hacerlo, aquí. Implemente su operación que consume mucho tiempo, y la comprobación de encendido / apagado, en el método doInBackground() y su actualización de la GUI en el método done() .

public enum State { RUNNING, STOPPED } public class ThreadSafeStateModel { private State state = State.STOPPED; public synchronized void stop() { state = State.STOPPED; } public synchronized void start() { state = State.RUNNING; } public boolean isRunning() { return state == State.RUNNING; } } public class ExpensiveProcessWorker extends SwingWorker<Void, Void> { private final ThreadSafeStateModel model; public ExpensiveProcessWorker(ThreadSafeStateModel model) { this.model = model; } @Override // Runs in background protected Void doInBackground() throws Exception { while (model.isRunning()) { // do one iteration of something expensive } return null; } @Override // Runs in event dispatch thread protected void done() { // Update the GUI } } public class StopButton extends JButton { public StopButton(final ThreadSafeStateModel model) { super(new AbstractAction("Stop") { @Override public void actionPerformed(ActionEvent e) { model.stop(); } }); } } public class StartButton extends JButton { public StartButton(final ThreadSafeStateModel model) { super(new AbstractAction("Start") { @Override public void actionPerformed(ActionEvent e) { model.start(); new ExpensiveProcessWorker(model).execute(); } }); } }

(Se podría hacer mucho para limpiar esto dependiendo de la aplicación real, pero se entiende la idea).


Esta es una razón bastante sencilla: mientras que Java está trabajando en su proceso que consume mucho tiempo, no puede actualizar la GUI. Solución: ejecute el proceso lento en un hilo separado. Hay muchas maneras de programar eso, y probablemente dependería un poco de cómo se escribe su programa.


No debe iniciar procesos de larga ejecución en el controlador de eventos de Swing porque congelará su GUI, ya lo sabe. :) Comience en un nuevo hilo. Solo necesita usar un SwingWorker si planea manipular la GUI desde el hilo de trabajo (porque Swing no es seguro para subprocesos).


El problema es que está haciendo un trabajo intensivo y lento en el mismo hilo responsable de actualizar la GUI. SwingWorker le permite mover tareas que llevan mucho tiempo a un hilo de ejecución separado, dejando así que el hilo de la interfaz de usuario lo desinhibe.

Sin embargo, agrega una complicación adicional: afinidad. Los métodos de llamada en los componentes de la interfaz de usuario generalmente requieren que lo haga desde el hilo de la interfaz de usuario. Por lo tanto, debe usar una funcionalidad especial para volver al subproceso de interfaz de usuario del subproceso de trabajo. SwingWorker también te da esta habilidad.

Te sugiero que leas esta documentación .