如何使用wait()和notifyAll()在GUI类和逻辑线程之间进行通信

时间:2013-03-19 05:46:16

标签: java multithreading swing event-dispatch-thread

我的程序中有3个主要类。第一个扩展applet(并充当我的主要),第二个是一个线程(实现Runnable),它处理与服务器的后端后勤/通信,第三个是JPanel类,它创建GUI(这是创建的)在一个新的主题中,通过调用main(applet)类中的SwingUtilities.invokeAndWait()

GUI类与后端线程进行多次通信,以获取要在屏幕上显示的数据。我的问题是GUI在后端类为它们提供数据之前显示其组件。所以,我想告诉GUI在另一个线程中调用一个方法,等待从中回听,然后显示其组件。我尝试了几种变体:

Object[] data = backend.Method1(); 
wait();
showComponents(data);

在GUI类上,并将notifyAll();放在后端线程的Method1的底部,但是我在netbeans中发出警告(在同步上下文之外调用Object.wait),程序崩溃在java.lang.illegalmonitorstateexception运行时。我尝试将synchronized关键字添加到这两种方法中,但这会导致程序冻结。

我(很明显,我很确定)只是学习多线程:我在这里误解了什么?

我感觉它的相关性是JPanel类没有实现runnable。

编辑:根据@MadProgrammer的建议,我正在使用SwingWorker,它似乎正在做这个伎俩。感谢您指点我,我以前从未听说过它们。

 final JLabel loading = new JLabel("loading");
    loading.setVisible(true);
    add(loading);

    SwingWorker sw = new SwingWorker<Map<String, ArrayList<Time[]>>, Void>() {
        @Override
        public Map<String, ArrayList<Time[]>> doInBackground(){
            System.out.println("doing");
            Map<String, ArrayList<Time[]>> toReturn = dbh.getTimes(specToPass);
            return toReturn;
        }

        public void done(){
            try {
                loading.setVisible(false);
                Map<String, ArrayList<Time[]>> weekMap = new HashMap(this.get());
                showTimes(weekMap);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } catch (ExecutionException ex) {
                  ex.printStackTrace();
            }
       }
    };
    sw.execute();

如果我没有正确实施,我希望你能告诉我。再次感谢。

1 个答案:

答案 0 :(得分:5)

不要。以任何方式阻止UI总是一个坏主意。这将使您的应用程序“挂起”并阻止UI更新(或响应用户的任何输入)。

这会给用户带来非常糟糕的体验。

相反,使用某种回调,允许数据线程根据需要将更新发送回UI。这应该允许UI在等待来自数据线程的数据时显示类似“等待”消息的内容。

您也可以使用SwingWorker来处理数据收集,但它可以简化您的所有需求,但它提供了将任何更新同步回UI的简单方法。

确保与UI的所有创建/修改/交互都在事件调度线程的上下文中完成。

请查看Concurrency in Swing了解详情

<强>更新

基本工作流程(恕我直言)应该看起来像......

  • UI - 从后台线程请求数据,传递侦听器接口 到后台线程(使用loadingDoneloadingFailed为exmaple
  • UI - 显示“加载”消息。可能会显示一个很好的动画GIF
  • 线程 - 加载数据时(假设一切顺利),打包数据并通过loadingDone方法将其传回去
  • 用户界面 - 调用loadingDone后,与EDT重新同步(使用SwingUtilities.invokeLater)并更新用户界面。

使用SwingWorker实际上会更简单,它会提供内置的进度更新