2012-02-29 41 views
1

我正在使用swing在Java中進行一個簡單的遊戲,並且在按下按鈕之後,我的GUI出現了偶爾凍結的問題(最有可能是線程問題),應該觸發一個按鈕切換JPanels。在Java(和SwingUtilities)中的GUI線程

我發佈了一個相關的線程here,它有關於我目前使用的實際代碼的更多細節(雖然我確實更新了倒計時並使其工作正常)。從答案到該線程,似乎使用SwingUtilities.invokeLater()invokeAndWait()可能是我需要解決的問題,但我不確定在我的代碼中哪裏有必要或確切地如何實現它。

我不太瞭解線程,可以使用我可以獲得的任何幫助(最好是稍微詳細的一些示例代碼)。讓我知道,如果任何進一步的細節將是有用的。

+2

你叫'的Thread.sleep(...)'在事件線程(在美國東部時間)? – 2012-02-29 22:34:22

+0

不,現在不行了。我重寫了以前那樣做的部分代碼。 – scaevity 2012-02-29 22:53:54

+0

@scae您可以分享更新對組件的響應的代碼嗎? – 2012-02-29 23:01:08

回答

3

參見:Tutorial: Concurrency in Swing

一般來說,在事件指派線程是一個單獨的線程,通過事件隊列,處理一次一個隆隆。

SwingUtilities.invokeLater(..) 

把一個Runnable此隊列。因此,當EDT完成隊列中的所有事情之前,它將由EDT處理(這就是爲什麼在隊列中睡覺會阻止重新繪製等其他事件的原因)。從EDT本身調用invokeLater(..)是相當不尋常的,儘管在某些情況下它很有用(通常作爲破解)。我認爲我在過去的6年中沒有合法使用SwingUtilities.invokeAndWait(..)。也許一次。

javax.swing.Timer可配置爲一次或定期啓動。當它觸發時,它會在美國東部時間隊列中放置一個事件。如果需要進行計算密集型處理,請考慮使用javax.swing.SwingWorker在另一個線程上執行計算,並以線程安全方式返回結果(這也相對較少)。

+0

感謝您的解釋。你能幫我弄清楚我的代碼在特定情況下有什麼問題,以及如何解決它(我不太確定EDT開始/結束的位置以及我需要使用invokeLater()來添加一些東西的位置到美國東部時間隊列)? – scaevity 2012-02-29 22:56:54

+1

嘗試創建演示您的問題SSCCE(http://sscce.org/)。提煉問題可能會幫助你自己解決問題,否則,將它發佈到此處,人們將能夠給你更具體的建議。 – kylewm 2012-02-29 23:10:42

+0

謝謝!我這樣做,事實證明這是我認爲是完全無關(非GUI的東西),導致凍結。 – scaevity 2012-03-01 01:53:12

0

好看點是docs。在你的情況,這說明了如何SwingUtilities.invokeLater()作品以及在何處使用它:

導致doRun.run()要在AWT事件 派發線程異步執行。 當應用程序 線程需要更新GUI時,應使用此方法。

因此,在修改GUI的操作中,您必須使用invokeLater方法來確保GUI不會凍結。

另一個很好的資源是Java教程。它們涵蓋了concurrency in Swing

+0

好吧,我知道我需要使用invokeLater()來通過向EDT隊列添加一些代碼來停止凍結,但是在我的代碼中,我需要執行此操作嗎?我嘗試用它添加到,我認爲是有意義幾個不同的地方,但沒有自己的想法固定的,所以我明顯缺失的(我的其他職位代碼示例/方案概要的樣子)的東西。 – scaevity 2012-02-29 22:59:42

0

如果你已經在你的GUI代碼中定義的一些工作,這樣

Runnable doWorkRunnable = new Runnable() { 
    @Override 
    public void run() { 
     doWork(); 
    } 
}; 

,會通過它連接到一個新的Thread

Thread t = new Thread(doWorkRunnable); 
t.start(); 

你執行你在GUI線程工作叫它,這會在Swing應用程序中造成問題。

相反試試這個(讓我提這僅僅是使用的例子)

SwingUtilities.invokeLater(doWorkRunnable); 

這使你的Runnable工人AWT事件隊列,並在以往活動完成後會執行它。

編輯:這是一個完整的例子,它執行從3到0的倒計時,然後在倒計時後做任何你想做的事情。

public class TestFrame extends JFrame { 

    private JPanel contentPane; 
    private final Timer timer; 
    private TimerTask[] tasks; 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      public void run() { 
       try { 
        TestFrame frame = new TestFrame(); 
        frame.setVisible(true); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 

    public TestFrame() { 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     setBounds(100, 100, 450, 300); 
     contentPane = new JPanel(); 
     contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 
     contentPane.setLayout(new BorderLayout(0, 0)); 
     final JLabel lblCountdown = new JLabel(); 
     contentPane.add(lblCountdown, BorderLayout.NORTH); 
     JButton btnStart = new JButton("Start"); 
     contentPane.add(btnStart, BorderLayout.SOUTH); 

     timer = new Timer(); 
     tasks = new TimerTask[4]; 

     setContentPane(contentPane); 

     for (int i = 0; i < 4; i++) { 
      final int count = i; 
      tasks[i] = new TimerTask() { 
       public void run() { 
        EventQueue.invokeLater(new Runnable() { 
         @Override 
         public void run() { 
          lblCountdown.setText(count + ""); 
         } 
        }); 
       } 
      }; 
     } 

     btnStart.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 

       for (int i = 0; i < 4; i++) { 
        timer.schedule(tasks[4 - i - 1], (1000 * i), (1000 * (i + 1))); 
       } 
       // add another timer.schedule(TimerTask) 
       // to execute that "move to game screen" task 
       TimerTask taskGotoGame = new TimerTask() { 
        public void run() { 
         timer.cancel(); 
         JOptionPane.showMessageDialog(null, "Go to game", "Will now", JOptionPane.INFORMATION_MESSAGE); 
         System.exit(0); 
        } 
       }; 
       // and schedule it to happen after ROUGHLY 3 seconds 
       timer.schedule(taskGotoGame, 3000); 
      } 
     }); 

    } 

} 
+0

感謝您的概述,但我已經知道我可能不得不使用invokeLater(),我只是不知道在哪裏,我正在尋找更具體到我的代碼的幫助(我已經提到過,我停止使用Thread.sleep(),所以這不會導致問題)。你能否看看我在我的問題中鏈接到的帖子中的實際代碼/程序大綱,並給我更多相關的反饋? – scaevity 2012-02-29 23:05:25

+0

我在幾個地方提到過,當程序成功切換JPanel時,倒計時工作得很好。問題在於面板最終會顯示倒計時,甚至變得可見(並且在此之前沒有GUI按鈕按下)。這是一個問題,即使我沒有實際訪問任何倒計時代碼,只是添加面板並嘗試使其可見(即在我的示例代碼中,如果我註釋掉levelPanel.start()調用它仍然凍結)。 – scaevity 2012-02-29 23:20:18

+0

我已經添加了一個完整的工作示例。我們在其中定義了4個'TimerTask',其中'Runnable's將'JLabel'更新爲特定的數字,另一個結束任務''Timer'停止,並且在倒計時完成後執行你想要做的事情。我遺漏了「切換到遊戲面板」部分。閱讀主要方法來發現要做什麼。 – 2012-03-01 00:29:06

0

我已經創建了一個WorkerThread類,它負責處理線程和GUI當前/主線程。我已經把的WorkerThread的構造()方法,我的GUI應用程序時,一個事件火開始XXXServer那麼所有線程激活和GUI工作smoothlly wihout凍結。看一看。 /** * 行動事件 * * @see java.awt.event.ActionListener#的actionPerformed(java.awt.event.ActionEvent中) */ 公共無效的actionPerformed(ActionEvent的AE){ log.info(」 actionPerformed begin ...「+ ae.getActionCommand());

try { 
     if (ae.getActionCommand().equals(btnStart.getText())) { 
      final int portNumber = 9990; 
      try { 

       WorkerThread workerThread = new WorkerThread(){ 
        public Object construct(){ 

         log.info("Initializing the XXXServer ..."); 
         // initializing the Socket Server 
         try { 
          XXXServer xxxServer = new XXXServer(portNumber); 
          xxxServer.start(); 
          btnStart.setEnabled(false);        
         } catch (IOException e) { 
          // TODO Auto-generated catch block 
          log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage()); 
          e.printStackTrace(); 
         } 
         return null; 
        } 
       };workerThread.start(); 
       } catch (Exception e) { 
        log.info("actionPerformed() Start button ERROR..." + e.getMessage()); 
        e.printStackTrace(); 
      } 


     } else if (ae.getActionCommand().equals(btnStop.getText())) { 
      log.info("Exit..." + btnStop.getText()); 
      closeWindow(); 
     } 

    } catch (Exception e) { 
     log 
      .info("Error in ServerGUI actionPerformed===" 
       + e.getMessage()); 
    } 

}