2012-02-29 74 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()); 
    } 

}