更新Swing时等待线程

时间:2013-01-08 18:26:26

标签: java multithreading swing real-time

我在应用程序中处理线程时遇到问题。它创建JFrame并启动一个新的Thread。最后一个将执行外部应用程序并更新GUI。然后

我有问题让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.");

    }

}

和Thread类:

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。

任何提示?

2 个答案:

答案 0 :(得分:1)

使用SwingUtilitied.invokeLater在线程内更新GUI,或者同步主变量!

http://www.vogella.com/articles/JavaConcurrency/article.html#concurrencyjava

也许已经足以使“状态”变得不稳定了?

答案 1 :(得分:1)

Swing是一个单线程环境。也就是说,有一个线程负责管理Swing UI的所有交互和更新 - 事件调度线程。

Swing的黄金法则是......

  • 不要阻止EDT(Thread.sleepThread#joinObject#wait,阻止IO和/或耗时的任务(以及其他)永远不应该从EDT中调用,这样做会阻止EDT调度事件和绘制更新(以及其他内容)
  • 仅在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就是凌乱的。更简单的解决方案实际上是使用SwingWorker。这有发布/处理方法,可以轻松更新UI和进度方法,可用于提供有关当前任务进度的反馈。

您可以使用它的done方法在工人完成时通知相关方。