El EDT (Event Dispatch Thread) de Java

De ChuWiki

Java procesa todos los eventos de ratón y teclado en un hilo aparte conocido como hilo de despacho de eventos (EDT Event dispatch Thread). Este hilo es también el encargado de repintar las ventanas cuando las movemos, redimensionamos o pasamos otras ventanas por encima.

El problema de este mecanismo es que si ante un evento de ratón o teclado realizamos una tarea que tarde mucho tiempo, durante todo ese tiempo el EDT está haciendo nuestra tarea y, por tanto, no está repintando las ventanas ni atendiendo a otros eventos de teclado o ratón.

El resultado es que mientras realizamos esa tarea, nuestra ventana no responde a eventos de teclado o ratón y no se repinta si pasamos otra ventana por encima. Este efecto dura hasta que terminemos de realizar nuestra tarea. Todo lo que hemos intentado hacer con la ventana con el ratón o teclado queda "encolado", de forma que cuando termina nuestra tarea, todo se repinta de golpe.

Un código como el siguiente, hace que nuestra ventana no se refresque durante diez segundos cuando pulsamos el botón

boton.addActionListener (new ActionPerformed()
{
   public void actionPerformed (ActionEvent e)
   {
      try
      {
         System.out.println("Me han pulsado");
         Thread.sleep(10000); //Tarea que consume diez segundos.
         System.out.println("Terminé");
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }
});

Para evitar esto, si prevemos que una tarea puede tardar un tiempo apreciable, es mejor lanzarla en un hilo separado según se pulsa el botón.

boton.addActionListener (new ActionPerformed()
{
   public void actionPerformed (ActionEvent e)
   {
      Runnable miRunnable = new Runnable()
      {
         public void run()
         {
            try
            {
               System.out.println("Me han pulsado");
               Thread.sleep(10000); //Tarea que consume diez segundos.
               System.out.println("Terminé");
            }
            catch (Exception e)
            {
               e.printStackTrace();
            }
         }
      }
      Thread hilo = new Thread (miRunnable);
      hilo.start();
   }
});

De esta última forma, se lanza un nuevo hilo y se libera inmediatamente el de EDT, por lo que el refresco de las ventanas seguirá funcionando y nuestra tarea se irá haciendo en "background".

Independiente del tiempo que preveamos que puede tardar una tarea, suele ser buena costumbre hacer esto siempre que desde el botón tengamos que acceder a otros ejecutables (bases de datos, servidores, sockets, web services, etc). El motivo es que aunque habitualmente puedan contestar rápido, ante problemas de red o caídas de dichos servidores, siempre es mejor protegerse y que nuestra interface de usuario no se quede bloqueada hasta que se produzca el fallo de conexión.


Barras de progreso[editar]

Suele ser habitual cuando hacemos una tarea larga que queramos mostrar una barra de progreso. Esa barra de progreso se repinta en el EDT. Por ello, si nuestra tarea que tarda está bloqueando dicho hilo de repintado, no veremos avanzar nuestra barra de progreso. Cuando termine nuestra tarea, veremos cómo de golpe la barra de progreso pasa al 100%.

Para evitar este efecto, debemos nuevamente evitar bloquear el hilo de repintado. Debemos realizar nuestra tarea en un hilo separado y desde ese hilo, según va avanzando la tarea, actualizar la barra de progreso

boton.addActionListener (new ActionPerformed()
{
   public void actionPerformed (ActionEvent e)
   {
      Runnable miRunnable = new Runnable()
      {
         public void run()
         {
            barraProgreso.setVisible(true);
            barraProgreso.setValor(0);
            try
            {
               System.out.println("Me han pulsado");
               primerPasoTarea();
               barraProgreso.setValor(33); // Al 33%
               segundoPasoTarea();
               barraProgreso.setValor(66); // Al 66%
               ultimoPasoTarea();
               barraProgreso.setValor(100); // Al 100%
               System.out.println("Terminé");
               barraProgreso.setVisible(false);
            }
            catch (Exception e)
            {
               e.printStackTrace();
            }
         }
      }
      Thread hilo = new Thread (miRunnable);
      hilo.start();
   }
});

SwingWorker[editar]

Java, a partir de su versión 1.6, nos ofrece la clase SwingWorker para ayudarnos a realizar este tipo de tareas: barras de progreso. Aquí puedes ver un Ejemplo sencillo con SwingWorker