2013-01-08 54 views
1

我在處理我的應用程序中的線程時遇到了問題。它創建JFrame並啓動一個新的線程。最後一個將執行外部應用程序並更新GUI。然後在更新Swing時等待線程

我有問題,使Main類等待第二個線程完成,但也要同時更新GUI。

這是我的例子(縮短):

class Main { 

    public int status; 

    public Main() { 

     // Creating GUI etc. 

     SwingUtilities.invokeLater(new Runnable() { 

      public void run() { 
       JDialog id = new JDialog(); 
       id.button.addMouseListener(new MouseListener()); // Calls generate() method 
      } 

     }); 

    } 

    public void generate() { 

     SwingUtilities.invokeLater(new Runnable() { 

      public void run() { 
       // Make changes to GUI 
      } 

     }); 

     GeneratorThread genTest = new GeneratorThread(this, 1, 1, 1); 
     genTest.start(); 

     //while (status == 0); 

     System.out.println("Next step."); 

    } 

} 

和線程類:

public class GeneratorThread extends Thread { 

protected Main main; 
protected int setSize, minValue, maxValue; 

public GeneratorThread(Main main, int setSize, int minValue, int maxValue) { 
    this.main = main; 
    this.setSize = setSize; 
    this.minValue = minValue; 
    this.maxValue = maxValue; 
} 

public void run() { 

    // Execute program etc. 
    // Change GUI from main in the same time 
      // About 3 seconds 

    main.status = 1; 

} 

} 

我在進步,我想檢查它是如何工作至今。雖然工作很好,但它以某種方式鎖定Swing,只有當GeneratorThread完成時,任何更改纔可見。我想實時更新GUI。我試過join(),效果是一樣的。我也嘗試wait()(在Main),但後來我得到IllegalStateMonitorException。

任何提示?

+4

請提供一個SSCCE。真的不清楚你在做什麼以及你想做什麼。 –

+0

很難用提供的代碼來告訴你想要達到的目標。雖然如果您想更新GUI,則意味着更新與正在執行的任務同步完成。你可以看看[SwingUtilities.invokeAndWait(...)](http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#invokeAndWait(java.lang.Runnable) ))。這裏是一個相關的[示例](http://stackoverflow.com/a/13004927/1057230) –

回答

1

Swing是一個單線程環境。也就是說,只有一個線程負責管理Swing UI的所有交互和更新 - 事件分派線程。

其中擺幅的黃金法則...

  • 不要堵塞EDT(Thread.sleepThread#joinObject#wait,塊IO和/或耗時的任務(其中包括)不應該從呼籲在美國東部時間),這樣做將阻止美國東部時間派遣事件和油漆更新(除其他事項外)
  • 只從EDT內創建/更新Swing UI元素。

這提出了一個問題......你如何「等待」一個線程?

最好的方法是使用Observer模式。基本上,您提供Thread以某種參考,它會調用以提供事件通知,如錯誤和完成...

這將需要您仔細考慮應用程序的設計,因爲您不能依靠簡單的A到B執行你的代碼。

例如...

public class TestThreadCallBack { 

    public static void main(String[] args) { 
     new TestThreadCallBack(); 
    } 

    public TestThreadCallBack() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (Exception ex) { 
       } 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new BorderLayout()); 
       frame.add(new TestPane()); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public interface ThreadCallBack { 

     public void threadCompleted(Runnable source); 

     public void threadFailed(Runnable source); 
    } 

    public class TestPane extends JPanel implements ThreadCallBack { 

     private JLabel message; 
     private JLabel dots; 
     private int count; 

     private Timer timer; 

     public TestPane() { 
      setLayout(new GridBagLayout()); 
      message = new JLabel("Running background task, please wait"); 
      dots = new JLabel(" "); 
      add(message); 
      add(dots); 

      timer = new Timer(250, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        count++; 
        if (count > 3) { 
         count = 0; 
        } 
        StringBuilder sb = new StringBuilder(3); 
        for (int index = 0; index < count; index++) { 
         sb.append("."); 
        } 
        for (int index = count; index < 3; index++) { 
         sb.append(" "); 
        } 
        dots.setText(sb.toString()); 
       } 
      }); 
      timer.setRepeats(true); 
      timer.setCoalesce(true); 
      timer.start(); 

      Thread thread = new Thread(new BackgroundTask(this)); 
      thread.start(); 

     } 

     @Override 
     public void threadCompleted(Runnable source) { 
      timer.stop(); 
      message.setText("Task completed successfully"); 
     } 

     @Override 
     public void threadFailed(Runnable source) { 
      timer.stop(); 
      message.setText("Task failed"); 
     } 
    } 

    public class BackgroundTask implements Runnable { 

     private ThreadCallBack callBack; 

     public BackgroundTask(ThreadCallBack callBack) { 
      this.callBack = callBack; 
     } 

     @Override 
     public void run() { 
      System.out.println("Background task underway..."); 
      try { 
       Thread.sleep(2000); 
      } catch (InterruptedException interruptedException) { 
      } 
      int result = (int) Math.round((Math.random() * 1)); 
      if (result == 0) { 
       callBack.threadCompleted(this); 
      } else { 
       callBack.threadFailed(this); 
      } 
     } 
    } 
} 

更新從其他Thread然後EDT內的UI是,很好,凌亂。更簡單的解決方案實際上是使用SwingWorker。它具有發佈/處理方法,可以方便地更新UI和進度方法,這些方法可用於提供有關當前任務進度的反饋。

您可以使用它的done方法在工作人員完成時通知相關方。