java - SwingWorker:在工作线程中创建 Swing 组件并添加到 EDT 中

标签 java multithreading swing swingworker

根据 SwingWorker Javadoc:

When writing a multi-threaded application using Swing, there are two constraints to keep in mind:

  • Time-consuming tasks should not be run on the Event Dispatch Thread. Otherwise the application becomes unresponsive.
  • Swing components should be accessed on the Event Dispatch Thread only.

因此,在使用 SwingWorker 时,我们必须仅在 done()process() 方法中访问 Swing 组件。

因此,我创建了一个示例应用程序,它以 block 的形式从数据库加载 3000 行,每个 block 有 50 行并显示它。

SwingWorker<List<Person>, Person> worker = 
   new SwingWorker<List<Person>, Person>() {

      protected List<Person> doInBackground() {
         List<Person> data = new ArrayList<>();
         while (hasMoreData()) {
            List<Person> chunk = loadFiftyRows();
            data.addAll(chunk);
            publish(chunk);
         }
         return data;
      }

      protected void process(List<Person> chunks) {
         for (Person person : chunks) {
            container.add(createPanelFor(person));
         }
         container.revalidate();
         container.repaint();
      }

   };
worker.execute();

由于,所有 swing 访问都必须在 EDT 上完成,我正在 process() 方法中为每个 person 创建新的 JPanel 实例。但是,该应用程序在启动时仍然相当繁重。在加载数据时它会卡住并且对事件的响应相当晚。

尽管我们一次发布 50 行,但在 EDT 上调用 process() 方法时, block 大小会变大,有时会达到 1000 行。我认为这是延迟的原因,在 EDT 上创建了 1000 个 JPanel 实例!

我觉得,创建 1000 个新的 JPanel 实例也是一项相当繁重的任务,需要在 EDT 上执行。 那么,我们不能在 doInBackground() 方法中创建这些 JPanel 实例,将其存储在集合中,然后在 process 方法中将这些已创建的 JPanel 实例添加到 container 中吗?

但是,它与“Swing 组件只能在事件调度线程上访问”的说法相矛盾!

此语句是否意味着已添加到 Swing 组件层次结构中的现有 Swing 组件而不是新创建的 Swing 组件?

最佳答案

Swing Threading Policy状态:

In general Swing is not thread safe. All Swing components and related classes, unless otherwise documented, must be accessed on the event dispatching thread. Typical Swing applications do processing in response to an event generated from a user gesture. For example, clicking on a JButton notifies all ActionListeners added to the JButton. As all events generated from a user gesture are dispatched on the event dispatching thread, most developers are not impacted by the restriction.

Where the impact lies, however, is in constructing and showing a Swing application. Calls to an application's main method, or methods in Applet, are not invoked on the event dispatching thread. As such, care must be taken to transfer control to the event dispatching thread when constructing and showing an application or applet. The preferred way to transfer control and begin working with Swing is to use invokeLater. The invokeLater method schedules a Runnable to be processed on the event dispatching thread.

您不应该在 EventDispatchThread 之外创建 Swing 组件,因为更新组件的模型会生成事件,更多 here

深化this answer可能会有所启发。

正如对您的问题“分页”的评论中提到的,也许“加载栏”可以解决您的问题。

关于java - SwingWorker:在工作线程中创建 Swing 组件并添加到 EDT 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29579842/

相关文章:

java - 在java中用double返回一个对象?

java - 我如何知道为 Sun JVM 启用了哪些默认设置?

ruby-on-rails - 防止ffmpeg接管stdout

c++ - 为什么多线程没有加速数组的简单复制?

Java - 使用文本区域将 JTable 设置为不可编辑

java - 在 ubuntu 15.10 上安装 Java

java - Selenium 网格: DevToolsActivePort file doesn't exist (unknown error: Chrome failed to start: exited abnormally)

c# - 如何捕获主线程可以监视的多线程应用程序中的事件?

java - 自动完成响应缓慢

java - 如何从字符串设置 JSpinner 的值?