2010-11-19 19 views
5

Ho questo ActionListener che viene chiamato nell'EDT. La mia funzione plot() è computazionalmente pesante, può facilmente richiedere cinque secondi. Ha reso la GUI bloccata come previsto. Ho aggiunto il codice SwingUtilities.invokeLater e si blocca ancora. La GUI non dovrebbe essere reattiva ora che sto generando un thread separato per il mio calcolo dell'heave?Perché la GUI rimane bloccata anche dopo aver usato 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(); 
        } 
       }); 
      } 
     }; 

risposta

15

Niente affatto. InvokeLater non sta producendo un nuovo thread. invokeLater exists to tell Swing explicitly "use the Event Dispatching Thread for this, but not right now". invoke e invokeLater esistono per consentire di eseguire operazioni che sono sicure solo per il thread Dispatching eventi da altri thread, non eseguendoli su quei thread, ma dicendo all'EDT di eseguirli.

ActionListener verrà eseguito molto rapidamente, generando la coda di invio eventi Runnable on Swing. Poi quando arriva così lontano, ci vorranno cinque secondi per eseguire la trama().

L'unica soluzione è quella di refactoring(). Utilizzare uno SwingWorker (o una strategia di multithreading simile, ma SwingWorker è probabilmente il migliore per questo) per spostare effettivamente la logica di plot() su un thread diverso. Quel thread non può disegnare nulla in modo sicuro perché non è il Thread di Dispatching dell'evento Swing, quindi tutte le sue operazioni di estrazione devono essere eseguite tramite invokeLater(). Per motivi di efficienza, dovresti provare a eseguire tutte le operazioni di disegno contemporaneamente, su un invokeLater(), usando i risultati memorizzati dal tuo calcolo.

+1

Anch'io voglio fare la trama run() del codice in una classe che estende SwingWorker, ed esegui quel codice computazionalmente pesante nella funzione doInBackground() e poi usa la funzione done() per aggiornare il mio display? La mia GUI sarà ancora attiva mentre il calcolo sta avendo luogo? – smuggledPancakes

+0

@ user464095: Sì, esattamente. – ColinD

+1

Bingo. Questo è ciò che SwingWorker fa ed è per. –

1

invokeLater aggiunge un'attività alla coda di lavoro delle GUI. Verrà richiamato dopo che tutte le altre attività sono state eseguite, tuttavia utilizza ancora il thread della GUI.

Suggerisco di utilizzare un ExecutorService.

Come suggerisce @Adam, qualsiasi disegno effettivo deve essere eseguito tramite invokeLater.

1

Non si visualizza ciò che è all'interno della funzione plot(), ma si deve non inserire alcun dipinto. Calcola ciò che vuoi nel nuovo thread e fai il dipinto nell'EDT. Per fare questo, è meglio usare SwingWorker

3

Stai facendo l'opposto di quello che pensi di essere. Invece di eseguire il tuo thread di calcolo al di fuori dell'EDT, lo chiami esplicitamente nello all'interno di it!

SwingUtilities.invokeLater() accoda il runnable per l'invocazione in un secondo momento, nell'EDT! Si desidera utilizzare SwingWorker invece.

1

Ecco cosa ho fatto per l'app della mia azienda, questo è un pseudo codice per motivi legali, ma la cosa fondamentale è che se lo schermo non risponde, riavvierà la GUI. Ogni volta che usi SwingUtilities per avviare l'EDT, nello stesso blocco di init, crea due thread watcher. Un thread eseguirà semplicemente un'azione sul thread EDT utilizzando le utilità Swing. Un altro thread monitorerà il primo thread per vedere se sente che il primo thread è reattivo. Il primo thread riconoscerà la reattività solo se può eseguire un comando molto semplice.

set isEDTCheck true durante l'esecuzione in modo normale, falso in modalità debug (altrimenti si ottiene costantemente riavviato.

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