2010-11-19 5 views
5

Ich habe diese ActionListener, die in der EDT aufgerufen wird. Meine plot() - Funktion ist rechenintensiv, es kann leicht fünf Sekunden dauern. Es hat die GUI wie erwartet hängen gelassen. Ich fügte den SwingUtilities.invokeLater Code hinzu und es hängt noch. Sollte die GUI nicht reagieren, wenn ich jetzt einen separaten Thread für meine Hebebeschleunigung erstelle?Warum bleibt meine GUI auch nach der Verwendung von SwingUtilities.invokeLater hängen?

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(); 
        } 
       }); 
      } 
     }; 

Antwort

15

Überhaupt nicht. InvokeLater erzeugt keinen neuen Thread. invokeLater exists to tell Swing explicitly "use the Event Dispatching Thread for this, but not right now". invoke und invokeLater existieren, damit Sie Operationen ausführen können, die nur für den Event Dispatching Thread von anderen Threads aus sicher sind - nicht indem Sie sie für diese Threads ausführen, sondern indem Sie dem EDT sagen, dass sie dies tun sollen.

Ihre Action wird sehr schnell laufen, die Runnable Veranstaltung auf Schwingen werfen Dispatching Warteschlange. Wenn es so weit ist, dauert es fünf Sekunden, um das Diagramm auszuführen().

Die einzige Problemumgehung besteht darin, plot() zu refaktorieren. Verwenden Sie eine SwingWorker (oder eine ähnliche Multithreading-Strategie, aber SwingWorker ist wahrscheinlich die beste Lösung dafür), um die Logik von plot() tatsächlich auf einen anderen Thread zu verschieben. Dieser Thread kann nicht sicher zeichnen, da es sich nicht um den Swing Event Dispatching-Thread handelt. Daher müssen alle Zeichenoperationen über invokeLater() ausgeführt werden. Aus Gründen der Effizienz sollten Sie versuchen, alle Zeichenoperationen gleichzeitig in einem invokeLater() auszuführen, wobei Sie die Ergebnisse aus Ihrer Berechnung verwenden.

+1

So kann ich die Handlung() Code läuft in einer Klasse machen will, die sich SwingWorker, und führen Sie diesen rechenintensiven Code in der Funktion doInBackground() aus und verwenden Sie dann die Funktion done(), um meine Anzeige zu aktualisieren. Wird meine GUI während der Berechnung noch aktiv sein? – smuggledPancakes

+0

@ user464095: Ja, genau. – ColinD

+1

Bingo. Dafür ist und ist SwingWorker. –

1

invokeLater fügen Sie der GUIs-Arbeitswarteschlange eine Aufgabe hinzu. Es wird aufgerufen, nachdem alle anderen Aufgaben ausgeführt wurden, verwendet jedoch weiterhin den GUI-Thread.

Ich schlage vor, Sie betrachten mit einem ExecutorService.

Wie @Adam vorschlägt, muss jede tatsächliche Zeichnung über invokeLater erfolgen.

1

Sie zeigen nicht, was in der plot() -Funktion ist, aber Sie sollten nicht setzen Sie keine Malerei in dort. Berechnen Sie, was Sie wollen, in dem neuen Thread und führen Sie das Malen im EDT durch. Um dies zu tun, ist es besser, verwenden SwingWorker

3

Sie tun das Gegenteil von dem, was Sie denken, Sie sind. Anstatt Ihren Berechnungsthread außerhalb des EDT auszuführen, nennen Sie ihn explizit innerhalb it!

SwingUtilities.invokeLater() reiht die runnable für den Aufruf zu einem späteren Zeitpunkt, in den EDT up! Sie möchten stattdessen SwingWorker verwenden.

1

Hier ist, was ich für meine Firma App gemacht habe, das ist ein Pseudo-Code aus rechtlichen Gründen, aber die Jist ist, dass, wenn der Bildschirm nicht mehr reagiert, wird es die GUI neu starten. Wenn Sie SwingUtilities zum Starten des EDT verwenden, erstellen Sie in demselben init-Block zwei Watcher-Threads. Ein Thread führt einfach eine Aktion für den EDT-Thread mithilfe von Swing-Dienstprogrammen durch. Ein anderer Thread überwacht den ersten Thread, um zu sehen, ob der erste Thread reagiert. Der erste Thread erkennt die Reaktionsfähigkeit nur, wenn er einen sehr einfachen Befehl ausführen kann.

Satz isEDTCheck auf true, wenn in der normalen Art und Weise, falsch im Debug-Modus ausgeführt wird (sonst werden Sie ständig neu gestartet bekommen.

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); 
     }