2010-11-19 10 views
5

EDTで呼び出されるActionListenerがあります。私のplot()関数は計算上重く、簡単に5秒かかることがあります。 GUIが期待どおりにハングアップしました。私はSwingUtilities.invokeLaterコードを追加し、それはまだハングアップします。私はヒーブ計算のために別のスレッドを生成しているので、GUIは応答してはいけませんか?SwingUtilities.invokeLaterを使用してもGUIが停止するのはなぜですか?

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

答えて

15

全くありません。 InvokeLaterは新しいスレッドを生成していません。 invokeLater exists to tell Swing explicitly "use the Event Dispatching Thread for this, but not right now". invokeとinvokeLaterは、他のスレッドからのイベントディスパッチスレッドに対してのみ安全な操作を実行できるようにするために存在します。これらのスレッドではなく、EDTに指示します。

あなたのActionListenerは非常に速く実行され、SwingのイベントディスパッチキューにRunnableをスローします。それがそれを得ると、プロットを実行するのに5秒かかる()。

唯一の回避策は、plot()をリファクタリングすることです。 SwingWorker(または同様のマルチスレッド戦略ですが、おそらくSwingWorkerがこれに最も適しています)を使用して、実際にplot()のロジックを別のスレッドに移動します。そのスレッドは、スイングイベントディスパッチスレッドではないため、安全に描画することはできません。そのため、描画操作はすべてinvokeLater()を使用して実行する必要があります。効率を上げるために、計算結果から1つのinvokeLater()を使用して、すべての描画操作を一度に実行するようにしてください。

+1

だから私は、拡張するクラスのプロット()のコードの実行を作りたいんSwingWorkerを実行し、その計算上重いコードをdoInBackground()関数で実行し、done()関数を使用してディスプレイを更新しますか?計算が実行されている間も、 – smuggledPancakes

+0

@ user464095:そうです。 – ColinD

+1

ビンゴ。これがSwingWorkerのやり方です。 –

1

invokeLaterタスクをGUIワークキューに追加します。他のすべてのタスクが実行された後に呼び出されますが、それでもGUIスレッドが使用されます。

ExecutorServiceを使用することをお勧めします。

@Adamが示唆するように、実際の描画はinvokeLaterを介して行う必要があります。

1

あなたはplot()関数の中身を表示しませんが、ではなく、に絵を入れてください。新しいスレッドに必要なものを計算し、EDTでペイントします。これを行うには、使用することをお勧めします。SwingWorker

3

あなたは自分の考えとは反対のことをしています。計算スレッドをEDT外で実行するのではなく、を明示的にと呼んでいます。

SwingUtilities.invokeLater()は、後で実行のためにrunnableをEDTで待ち行列に入れます。代わりにSwingWorkerを使用します。

1

これは私の会社のアプリケーションのためのものです。これは法律上の理由から疑似コードですが、画面が応答しない場合はGUIを再起動します。 SwingUtilitiesを使用してEDTを開始するときは、同じinitブロックで2つのウォッチャースレッドを作成します。 1つのスレッドは、Swingユーティリティを使用してEDTスレッドでアクションを実行します。別のスレッドが最初のスレッドを監視して、最初のスレッドが応答しているかどうかを確認します。最初のスレッドは非常に単純なコマンドを実行できる場合にのみ、応答性を認識します。デバッグモードで偽

通常の方法で実行している場合にtrueにisEDTCheckを設定

は、(そうでなければ、常に再起動しますよ。

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); 
     } 
関連する問題