2010-11-19 15 views
5

Tengo este ActionListener que se llama en el EDT. Mi función plot() es computacionalmente pesada, puede tomar fácilmente cinco segundos. Hizo que la GUI se cuelgue como se esperaba. Agregué el código SwingUtilities.invokeLater y aún cuelga. ¿No debería la GUI ser receptiva ahora que estoy generando un hilo separado para mi cálculo?¿Por qué mi GUI aún se cuelga incluso después de usar SwingUtilities.invokeLater?

final ActionListener applyListener = new ActionListener() 
     { 
      @CommitingFunction 
      public void actionPerformed(ActionEvent arg0) 
      { 
       /*Don't do plotting in the EDT :)*/ 
       SwingUtilities.invokeLater(new Runnable() 
       { 
        public void run() 
        { 
         plot(); 
        } 
       }); 
      } 
     }; 

Respuesta

15

No, en absoluto. InvokeLater no está produciendo un nuevo hilo. invokeLater exists to tell Swing explicitly "use the Event Dispatching Thread for this, but not right now". invoke y invokeLater existen para permitirle realizar operaciones que solo son seguras para el subproceso de envío de eventos desde otros hilos, no al hacerlos en esos hilos, sino al decirle al EDT que los haga.

Su ActionListener funcionará muy rápidamente, lanzando el Ejecutable en caso de oscilación envío de cola. Luego, cuando llegue tan lejos, tomará cinco segundos ejecutar el diagrama().

La única solución consiste en refactorizar el trazado(). Use un SwingWorker (o una estrategia similar de subprocesos múltiples, pero SwingWorker es probablemente el mejor para esto) para mover realmente la lógica de plot() a un hilo diferente. Ese hilo no puede extraer nada con seguridad porque no es el hilo de envío del evento Swing, por lo que todas sus operaciones de dibujo deben realizarse a través de invokeLater(). Por razones de eficiencia, debe intentar realizar todas las operaciones de dibujo a la vez, en un invokeLater(), utilizando los resultados almacenados de su cálculo.

+1

yo también quiero hacer la trama() ejecutar código en una clase que se extiende SwingWorker, y ejecuta ese código computacionalmente pesado en la función doInBackground() y luego usa la función done() para actualizar mi pantalla ¿Mi GUI seguirá activa mientras se lleva a cabo el cálculo? – smuggledPancakes

+0

@ user464095: Sí, exactamente. – ColinD

+1

Bingo. Eso es lo que SwingWorker hace y es para. –

1

invokeLater agrega una tarea a la cola de trabajo de la GUI. Se invocará después de que se hayan realizado todas las demás tareas, sin embargo, todavía se utiliza el hilo de la GUI.

Le sugiero que consulte el uso de un ExecutorService.

Como sugiere @ Adam, cualquier dibujo real que hace necesario hacer a través de invokeLater.

1

No muestra qué hay dentro de la función plot(), pero debería no poner cualquier pintura allí. Calcule lo que quiera en el nuevo hilo y haga la pintura en el EDT. Para ello, es mejor utilizar SwingWorker

3

Estás haciendo lo contrario de lo que crees que eres. En lugar de ejecutar su hilo de cálculo fuera del EDT, lo llama explícitamente dentro de!

SwingUtilities.invokeLater() pone en cola el ejecutable para la invocación en un momento posterior, en el EDT! Desea utilizar SwingWorker en su lugar.

1

Esto es lo que hice para la aplicación de mi compañía, esto es un pseudo código por razones legales, pero la cuestión es que si la pantalla no responde, reiniciará la GUI. Siempre que use SwingUtilities para iniciar el EDT, en ese mismo bloque de inicio, cree dos hilos de observador. Un hilo simplemente realizará una acción en el hilo EDT usando las utilidades Swing. Otro hilo supervisará el primer hilo para ver si siente que el primer hilo responde. El primer subproceso solo reconocerá la capacidad de respuesta si puede realizar un comando muy simple.

conjunto isEDTCheck true cuando se ejecuta en modo normal, falsa en modo de depuración (de lo contrario obtendrá constantemente reiniciado.

if (isEDTCheck) { 
     new Thread("EDTHeartbeat") { 
      @Override 
      public void run() { 
       Runnable thisThingYouDo = new Runnable() { 
        public void run() { 
         int x = 0; 
        } 
       }; 
       while (true) { 
        // first thread says we are waiting, aka bad state 
        edtwait=true; 
        try { 
         javax.swing.SwingUtilities.invokeAndWait(thisThingYouDo); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } catch (InvocationTargetException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
        // first thread says we are not waiting, good state 
        edtwait=false; 
        try { 
         Thread.sleep(5000); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 
      } 
     }.start(); 

     new Thread("EDTValidator") { 
      @Override 
      public void run() { 
       while (true) { 
        // is first thread in bad state? 
        if (edtwait) { 
         try { 
          Thread.sleep(3000); 
          // after 3 seconds are we still in bad state? if so, get rid of initial frame, pop up a dialog box in AWT that does no commands 
          if (edtwait) { 
           mainFrame.setVisible(false); 
           new Dialog(); 
          } catch (InterruptedException e) { 
          // TODO Auto-generated catch block 
          e.printStackTrace(); 
         } 
        } 
        try { 
         Thread.sleep(1000); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 
      } 
     }.start(); 
    } 


    public class Dialog extends Frame { 
    private static final int WIDTH = 400; 
    private static final int HEIGHT = 300; 
    Frame f = null; 
    public Dialog() { 
     f = this; 
     hasSomethingBeenEntered=false; 
     this.setTitle("APP PROBLEM DETECTED"); 
     this.setSize(WIDTH, HEIGHT); 
     this.setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth() - myapp.width, 0); 
     Panel p1 = new Panel() { 
      @Override 
      public void paint(final Graphics g) { 
       int left = Dialog.WIDTH/2 - 45; // don't use WIDTH shadowed by Panel class 
       int top = Dialog.HEIGHT/2 - 20; // same as above 
       g.drawString("APP HAS DETECTED A PROBLEM", left, top); 
      } 
     }; 
     this.add("Center", p1); 

     this.setAlwaysOnTop(true); 
       TextArea tb = new TextArea("APP HAS DETECTED A MAJOR PROBLEM\nIT WILL NOW RESTART IN 5 SECONDS"); 
     this.add(tb); 
     this.setVisible(true); 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     restartApp(); 

    } 

    private void restartApp() { 
      Runtime.getRuntime().exec("cmd /c start cmd.exe /K \"cd C:\\Progra~1\\Common~1 && C:\\Progra~1\\Common~1\\MyAppDir\\myjavaapp.jar\""); 
      System.exit(0); 
     } 
Cuestiones relacionadas