带有进度和线程噩梦的 Java 异步数据加载

标签 java swing user-interface asynchronous load

全部,

我们在应用程序中加载数据时遇到问题。当前实现的目的是为数据加载提供异步机制。

我基本上想知道是否有任何好的模式或实践。

该应用程序有许多不同的 UI 组件,它们没有以标准方式进行编码。它都是从配置(即 xml)生成的。数据加载是通过点击日历触发的,日历会向数据加载器发送异步消息,其中大约有 6 个实例(同一类)从数据库加载数据。一旦数据被加载和处理,数据加载器类就会将表数据异步发送到要显示的 JTable。

由于数据加载可能需要一秒左右到 1 或 2 分钟,因此需要一个进度对话框。编写了一个额外的类来在开始/完成数据加载时监听来自数据加载器的事件。当任何数据加载器开始加载时,会显示一个对话框,指示正在加载数据。当最后一个数据加载器完成后,对话框应该被隐藏。

当前问题/问题

会出现2个问题:

  • 有时,即使所有数据加载器都已完成,对话框仍会保持可见。
  • 其他时候,数据可能不会显示在表格中。

  • 我们曾尝试在进度的显示和隐藏方法周围添加synchronized关键字,但仍然得到类似的发布。我认为某种重写可能是有序的,但我想更多地了解异步 Java 模式。该机制需要更加稳健和可靠。

    当前进程

    如果您对我们当前的流程感兴趣,我已尝试总结以下事件。这可能会揭示设计/实现中的问题。
  • 用户点击日历中的某一天。

    日历组件向每个数据加载器实例发送一条消息,告诉它们开始加载。此消息在单独的线程 (2) 上从日历异步发送。
  • 数据加载器接收加载消息

    每个 DataLoader 负责加载相关数据并将其传递到要显示的表中。

    每个数据加载器都有一个加载数据的线程实例。如果当前线程加载实例已设置并且正在加载数据,则加载将中止——这可能是由于快速单击日历中的日期而发生的。一旦数据加载线程空闲或为空,就会创建并启动一个新的线程实例。

    然后数据加载线程执行数据访问和处理。当它启动时,会通知 DataLoadDialog 类 (3)。这个 DataLoadDialog 基本上向 EventListenerList 添加了一个监听器(DataLoaderListener 扩展了 EventListener)。当数据线程加载时,会调用 DataLoaderListener.loadStart,它由 DataLoadDialog 选取。当线程加载并处理数据时,将调用 DataLoaderListener.loadComplete。这是在 DataLoadDialog 中再次选取的。

    现在数据已加载,DataLoader 将数据发送到要显示的表中。
  • 通知 DataLoadDialog 加载状态

    DataLoadDialog 有一个对每个数据加载器的引用。每个数据加载器都有一个状态,它通过一个 getter 访问,它返回 Idle 或 Loading 的枚举。每当 DataLoadDialog 收到 loadStart 或 loadComplete 的调用时,它会根据数据加载线程的状态确定是否显示/隐藏进度对话框。

    进程对话框是一个单例静态实例,它是在创建类时创建的(来自配置 xml)。

  • 任何帮助,将不胜感激。如果我遗漏了任何内容,请添加评论,我会更新问题。

    谢谢,

    安德斯

    最佳答案

    这些问题听起来像是违反了基本 Swing 规则:所有组件访问都必须在 EDT 上进行。确保所有用户反馈(显示/隐藏对话框、更新流程状态、更新表数据)都在 EDT 上触发。

    一个有用的类是 SwingWorker,它的 api 文档非常广泛,并且有如何直接访问模型/组件的示例。

    一些伪代码片段展示了如何通过 propertyChangeEvent 传递中间数据,将 worker 与实际 ui 分离一点:

    // on user click (here we are on EDT), 
    // open the dialog, create the loader, add a PropertyChangeListener and start 
    showProcessDialog();
    MySwingWorker worker = new MySwingWorker();
    PropertyChangeListener l = new PropertyChangeListener() {
         public void propertyChanged(....) {
             if (StateValue.DONE == evt.getNewValue) {
                 closeProcessDialog();
             }
             if ("chunkAvailable".equals(evt.getPropertyName()) {
                 addChunkToTable((ChunkType) evt.getNewValue());
             }
         }    
    }
    worker.addPropertyChangeListener(l);
    worker.execute();
    
    // custom implementation of SwingWorker
    public MySwingWorker extends SwingWorker<ResultType, ChunkType> {
    
        @Override
        protected  <ResultType> doInBackground() {
             // here we are in the worker thread 
             // start the actual loaders 
             Dataloaders loaders = ....
             // process their result/s 
             ResultType endResult = ...;
             while (...) {
                 ChunkType intermediateResult = ....
                 // possibly produce some end result
                 endResult = ... 
                 // publish the intermediate chunk 
                 publish(intermediateResult); 
             }
             return endResult; 
        }
    
        @Override
        protected void process(List<ChunkType> chunks) {
             // here we are on the EDT, so it's safe to either notify swing listener
             // or access swing components/models directly (as shown in the api/tutorial example) 
             for(ChunkType chunk: chunks) {
                 PropertyChangeEvent e = new PropertyChangeEvent(this, "chunkAvailable", null, chunk);
                 getPropertyChangeSupport().firePropertyChange(e);
             } 
        }
    
    }
    

    关于带有进度和线程噩梦的 Java 异步数据加载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8758213/

    相关文章:

    java - BorderLayout 的 Swing GUI 问题

    java - 我应该在 netbeans 中使用什么类型的表单?

    java - 来自标准 Java SE 应用程序的 Jetty

    java - 读取文件 - 哪种方法是可测试的?

    Java组合框 Swing

    java - 在 Java 中的 JList 中设置边框

    java - 带有 JTextArea 而不是文本字段的 JOptionPane?

    java - 从构造函数构建 GUI。 [设计决策]

    java - 如何在 java 运行时解析环境变量

    java - 数据行与正则表达式模式不匹配