2010-11-19 2 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는 다른 스레드의 Event Dispatching Thread에 대해서만 안전한 작업을 수행 할 수 있도록하기 위해 존재합니다. 이러한 스레드에서 수행하는 것이 아니라 EDT에 수행하도록 지시합니다.

ActionListener가 매우 빠르게 실행되어 Runnable을 Swing의 이벤트 발송 대기열에 던집니다. 그런 다음 그 거리에 도달하면 플롯을 실행하는 데 5 초가 걸립니다().

유일한 해결 방법은 리팩토링을하는 것입니다(). SwingWorker (또는 이와 유사한 다중 스레딩 전략을 사용하지만 SwingWorker가 아마도 이것에 가장 적합 함)을 사용하여 plot() 논리를 다른 스레드로 실제로 옮길 수 있습니다. 이 스레드는 Swing Event Dispatching Thread가 아니기 때문에 아무 것도 그릴 수 없으므로 모든 draw 작업은 invokeLater()를 통해 수행되어야합니다. 효율성을 위해 계산에서 저장된 결과를 사용하여 하나의 invokeLater()에서 모든 그리기 작업을 한 번에 수행해야합니다.

+1

그래서 확장하는 클래스에서 플롯() 코드 실행을 만들고 싶어 할 SwingWorker를 실행하고 doInBackground() 함수에서 계산량이 많은 코드를 실행 한 다음 done() 함수를 사용하여 디스플레이를 새로 고칩니다. 계산이 진행되는 동안 내 GUI가 계속 활성화됩니까? – smuggledPancakes

+0

@ user464095 : 가능합니다. – ColinD

+1

빙고. 이것이 바로 SwingWorker가하는 일입니다. –

1

invokeLater GUI 작업 큐에 작업을 추가하십시오. 다른 모든 작업이 수행 된 후에 호출되지만 GUI 스레드를 계속 사용합니다.

ExecutorService를 사용하는 것이 좋습니다.

@Adam이 제안했듯이 실제 드로잉은 invokeLater를 통해 수행되어야합니다.

1

당신은 무엇이 plot() 함수 안에 있는지 표시하지 않지만 이 아니라면에 그림을 넣어야합니다. 새 스레드에서 원하는 것을 계산하고 EDT에서 페인팅을 수행하십시오. 이렇게하려면 사용하는 것이 더 좋습니다 SwingWorker

3

당신이 생각하는 것과는 정반대의 행동을하고 있습니다. EDT 외부에서 계산 스레드를 실행하는 대신 명시 적으로 내에서 이내에 호출해야합니다!

SwingUtilities.invokeLater()는 나중에 실행을 위해 실행 파일을 EDT! 대신 SwingWorker을 사용하고 싶습니다.

1

내 회사의 앱에 대한 설명은 다음과 같습니다. 법적 사유로 인해 일부 유사 코드이지만, 화면이 응답하지 않으면 GUI가 재부팅됩니다. SwingUtilities를 사용하여 EDT를 시작할 때마다 동일한 init 블록에서 두 개의 감시자 스레드를 만듭니다. 한 스레드는 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); 
     }