2012-01-06 142 views
3

全部,Java异步数据加载进度和Threadding噩梦

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

我基本上想知道是否有任何良好的模式或做法。

该应用程序具有许多不以标准方式编码的不同UI组件。它全部由配置生成(即xml)。点击日历可以触发数据加载,该日历会向数据加载器发送异步消息,其中大约有6个实例(同一个类),用于加载数据库中的数据。一旦数据被加载并处理完毕,数据加载器类就会将表数据异步地发送到它将要显示的JTable中。

由于数据加载可能需要从第二个左右到第一个或第二个分钟,需要进度对话框。当启动/完成数据加载时,编写了一个额外的类来侦听来自数据加载器的事件。当任何数据加载器开始加载时,将显示一个对话框,指示数据已加载。当最后一个数据加载器完成时,对话框应该隐藏。

当前问题/问题

有2个问题出现:

  • 有时候,对话框将保持可见,即使所有的数据装载机已经完成。
  • Othertimes,数据可能不会显示在表格中。

我们试图徒劳地在显示和隐藏进度的方法周围添加synchronized关键字,但仍然得到类似的问题。我认为某种重写可能是有序的,但我想了解更多有关异步Java模式的内容。该机制需要更强大和可靠。

当前进程

如果你的兴趣在我们目前的过程中,我试图总结下面的事件。这可能会对设计/实施中的问题有所了解。

  1. 用户在日历中点击日。

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

  2. 数据加载器接收负载信息

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

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

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

    现在数据已加载,DataLoader将数据发送到要显示的表。

  3. 通知负载状态

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

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

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

感谢,

Andez

回答

0

我看着SwingWorker的。我通过例子工作,但想法是从SwingWorker产生每个DataLoader - 我想。

我决定不这样做,并发现DataLoadDialog类和Swing JDialog之间的交互进行方式的问题。我简化了我的例子太多。还有两个额外的组合也触发了数据加载。一个组合将迫使3个数据加载器开始加载,另一个组合加载另外3个数据加载器。这会迫使我添加额外的配置,由于系统写入的方式而会变得笨拙。

我最初尝试将synchronized关键字添加到loadStart和loadComplete中,但仍未解决问题。我也有invokeAndWait的对话框显示/隐藏的地方。我希望这可以防止多个线程尝试显示/隐藏进度 - 但事实并非如此。我用invokeLater替换了invokeAndWait,这似乎起到了诀窍的作用。

我完全同意SwingWorker的,像这样的情况下更应该在设计上进行布局 - 这是我们不擅长在这里 - 我讨厌。整个并发解决方案需要大约20分钟才能同步加载初始数据--1个加载程序依次从数据库中检索数据。加载的数据量非常可怕 - 它也必须通过某种调节。

Andez

3

的问题听起来像是违反底座摆规则:所有的组件访问必须在美国东部时间发生。确保在EDT上触发所有用户反馈(显示/隐藏对话框,更新过程状态,更新表格数据)。

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

展示如何通过通过一个PropertyChangeEvent传递的中间数据脱钩工人从实际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); 
     } 
    } 

} 
+0

谢谢kleopara。将研究它。我听到前面提到的SwingWorker的,只是需要让我的头周围以及它如何融入我们的应用程序作为一个整体:-S – Andez 2012-01-09 12:06:50

+0

@kleopatra:我可以补充一下? 关于SwingWorker的使用,它有点儿奇怪,但在我使用Netbeans进行调试之后...太多的SwingWorker-pool-thread活着,这是它应该发生的事情吗? – gumuruh 2012-06-05 15:15:46