Java中的GUI线程(和SwingUtilities)

时间:2012-02-29 22:30:59

标签: java multithreading swing user-interface freeze

我正在使用swing制作一个简单的Java游戏,并且在按下按钮后,我的GUI会偶尔出现问题(很可能是由于线程问题)。应该触发JPanels中的切换。

我发布了一个相关的帖子here,其中有关于我目前正在使用的实际代码的更多详细信息(虽然我确实更新了倒计时并使其工作正常)。从该线程的答案来看,似乎使用SwingUtilities.invokeLater()invokeAndWait()可能是我解决问题所需要的,但我不确定我的代码在哪里是必要的或者究竟如何实现它

我对线程知之甚少,可以使用任何帮助(最好是有些详细,并附带一些示例代码)。如果有任何进一步的细节有用,请告诉我。

5 个答案:

答案 0 :(得分:3)

请参阅:Tutorial: Concurrency in Swing

一般来说,Eve​​nt Dispatch Thread是一个单一的线程,通过事件队列,一次处理一个。

SwingUtilities.invokeLater(..) 

将Runnable放在此队列中。因此当EDT完成队列之前的所有事情时,它将由EDT处理(这就是为什么在队列上睡觉会阻止其他事件,如重新绘制)。从EDT本身调用invokeLater(..)是相对不常见的,尽管有些情况下它很有用(通常是作为黑客)。在过去的6年里,我认为我没有合法使用SwingUtilities.invokeAndWait(..)。也许一次。

javax.swing.Timer可以配置为触发一次或定期触发。当它触发时,它会在EDT队列上放置一个事件。如果您需要完成计算密集型处理,请考虑使用javax.swing.SwingWorker在另一个线程上进行计算,并以线程安全的方式返回结果(这也相对较少)。

答案 1 :(得分:0)

值得注意的是docs。在您的情况下,这解释了SwingUtilities.invokeLater()如何工作以及在何处使用它:

  

导致doRun.run()在AWT事件上异步执行   调度线程。 应用程序时应使用此方法   线程需要更新GUI

因此,在修改GUI的操作中,必须使用invokeLater方法确保GUI不会冻结。

另一个很好的资源是Java教程。它们涵盖concurrency in Swing

答案 2 :(得分: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);
            }
        });

    }

}

答案 3 :(得分:0)

我创建了一个WorkerThread类来处理Threads和GUI current / main thread。我已经将我的GUI应用程序放在WorkerThread的construct()方法中,当一个事件触发启动XXXServer然后所有线程都被激活并且GUI工作平稳地没有冻结。看一看。 / **      *行动事件      *      * @see java.awt.event.ActionListener #actionPerformed(java.awt.event.ActionEvent)      * /     public void 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());
    }

}

答案 4 :(得分:0)

为了在现有WorkerThread中调用动作,可以使用SwingUtilities.invokeLater()直观地将用户定义的事件发送给JFrame的actionPerformed()方法,

>>> dictionary = {
...     "Name": "Eva",
...     "Surname": "Schwartz",
...     "babies": [
...         {
...             "Name": "Klara",
...             "age": 6
...         },
...         {
...             "Name": "Nikola",
...             "age": 8
...         }
...     ]
... }
>>> baby_names = [baby["Name"] for baby in dictionary["babies"]]
>>> baby_names
['Klara', 'Nikola']

现在,任何线程中调用的TestFrame.invokeLater()将在现有WorkerThread中的TestFrame.actionPerformed()中进行处理。