2

我在处理应用程序中的线程时遇到问题。它创建 JFrame 并启动一个新线程。最后一个将执行外部应用程序并更新 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.");

    }

}

和线程类:

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()(on Main),但后来我得到了 IllegalStateMonitorException。

有什么提示吗?

4

2 回答 2

1

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

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

也许让“状态”变得不稳定就足够了?

于 2013-01-08T18:41:50.400 回答
1

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

Swing 的黄金法则包括...

  • 不要阻塞 EDT(永远不应从 EDT 中调用Thread.sleepThread#joinObject#wait、阻塞 IO 和/或耗时的任务(等等)),这样做会阻止 EDT 调度事件和绘制更新(等等)
  • 仅在 EDT 中创建/更新 Swing UI 元素。

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

最好的方法是使用观察者模式。基本上,您提供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另一个内部更新 UI,然后是 EDT,嗯,很混乱。一个更简单的解决方案实际上是使用SwingWorker. 它具有可轻松更新 UI 的发布/处理方法和可用于提供有关当前任务进度的反馈的进度方法。

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

于 2013-01-08T22:44:43.397 回答