更新 Swing 时等待线程 [英] Waiting for thread while updating Swing

查看:59
本文介绍了更新 Swing 时等待线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have problem with handling threads in my application. It creates JFrame and starts a new Thread. Last one will execute external application and update GUI. Then

我有问题让 Main 类等待第二个线程完成,但同时更新 GUI.

I have problem to make Main class to wait for second thread to finish, but also to update GUI simultaneously.

这是我的示例(已缩短):

Here's my example (shortened):

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.

I'm in progress and I wanted to check how it works so far. While worked nice, but it locks Swing somehow and any changes are visible only when GeneratorThread finishes. I would like to update GUI in the real time.

我试过join(),效果都一样.我也尝试过 wait()(在 Main 上),但后来我得到了 IllegalStateMonitorException.

I've tried join(), effects are the same. I also tried wait() (on Main), but then I got IllegalStateMonitorException.

有什么提示吗?

推荐答案

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

Swing is a single threaded environment. That is, there is a single thread responsible for managing all the interactions and updates to the Swing UI - the Event Dispatching Thread.

Swing 的黄金法则包括...

Among the golden rules of Swing are...

  • 不要阻塞 EDT(Thread.sleepThread#joinObject#wait、阻塞 IO 和/或耗时绝不能从 EDT 中调用任务(以及其他任务),这样做会阻止 EDT 分派事件和绘制更新(以及其他内容)
  • 仅在 EDT 中创建/更新 Swing UI 元素.
  • DON'T block the EDT (Thread.sleep, Thread#join, Object#wait, block IO and/or time consuming tasks (among others) should never be called from within the EDT), doing so will stop the EDT from dispatching events and paint updates (amongst other things)
  • ONLY create/update Swing UI elements from within the EDT.

这就提出了一个问题……你如何等待"一个线程?

This raises a question...how do you "wait" for a thread?

最好的方法是使用观察者模式.基本上,您为 Thread 提供某种引用,它将调用该引用以提供事件通知,例如错误和完成...

The best way is use an Observer pattern. Basically, you provide the Thread with some kind of reference that it will call to provide notification of events, such as errors and completion...

这需要您非常仔细地考虑应用程序的设计,因为您不能依赖简单的 A 到 B 执行代码.

This will require you to think very carefully about the design of your applications, as you can not rely on a simple A to B execution of your code.

例如...

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);
            }
        }
    }
}

在除 EDT 之外的 Thread 中更新 UI 是很麻烦的.一个更简单的解决方案实际上是使用 SwingWorker.这有发布/处理方法,可以轻松更新 UI 和进度方法,可用于提供有关当前任务进度的反馈.

Updating the UI from within a Thread other then the EDT is, well, messy. An easier solution would actually be to use a SwingWorker. This has publish/process methods that make easy to update the UI and progress methods that can be used to provide feedback about the progress of the current task.

您可以使用它的 done 方法在 worker 完成时通知相关方.

You can use it's done method to notify interested parties when the worker has completed.

这篇关于更新 Swing 时等待线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆