所有
我们的应用程序中存在数据加载问题。当前实现的目的是为数据加载提供异步机制。
我基本上想知道是否有任何良好的模式或做法。
该应用程序具有许多不以标准方式编码的不同UI组件。它全部由配置(即xml)生成。通过单击日历来触发数据加载,日历将向数据加载器发送异步消息,其中大约有6个实例(同一类)将从数据库加载数据。一旦加载并处理了数据,数据加载器类就会将表数据异步发送到要显示的JTable。
由于数据加载可能需要大约一秒钟到1或2分钟,因此需要一个进度对话框。编写了一个附加类,用于在启动/完成数据加载时侦听来自数据加载器的事件。当任何数据加载器开始加载时,将显示一个对话框,指示数据正在加载。当最后一个数据加载器完成时,应隐藏该对话框。
当前问题/问题
发生了两个问题:
我们试图在显示和隐藏进度的方法周围添加synchronized关键字,但仍然得到类似的发布。我认为某些重写可能是有序的,但我想了解更多关于异步java模式的内容。该机制需要更加强大和可靠。
当前流程
如果您对我们当前的流程感兴趣,我已尝试总结以下事件。这可能会对设计/实施中的问题有所了解。
用户点击日历中的日期。
日历组件向每个数据加载器实例发送一条消息,告诉他们开始加载。此消息在日历上以异步方式(2)发送。
数据加载器接收加载消息
每个DataLoader负责加载相关数据并将其传递给要显示的表。
每个数据加载器都有一个加载数据的线程实例。如果设置了当前线程加载实例并且正在加载数据,则中止加载 - 这可能是通过快速单击日历中的天数来实现的。一旦数据加载线程空闲或为空,就会创建并启动一个新的线程实例。
然后,数据加载线程执行数据访问和处理。启动时,会通知DataLoadDialog类(3)。此DataLoadDialog基本上将一个侦听器(DataLoaderListener扩展EventListener)添加到EventListenerList。加载数据线程时,将调用DataLoaderListener.loadStart,由DataLoadDialog拾取。当线程加载并处理数据时,将调用DataLoaderListener.loadComplete。这再次在DataLoadDialog中被选中。
现在数据已加载,DataLoader将数据发送到要显示的表。
通知负载状态的DataLoadDialog
DataLoadDialog具有对每个数据加载器的引用。每个数据加载器都有一个状态,可以通过getter访问,该getter返回一个空闲或加载的枚举。每当DataLoadDialog收到loadStart或loadComplete的调用时,它都会根据数据加载线程的状态来确定是否显示/隐藏进度对话框。
进程对话框是一个单例静态实例,它是在创建类时创建的(来自配置xml)。
任何帮助将不胜感激。如果我遗漏了任何内容,请添加评论,我会更新问题。
谢谢,
Andez
答案 0 :(得分:3)
这些问题听起来像违反基本Swing规则:所有组件访问必须在EDT上发生。确保在EDT上触发所有用户反馈(显示/隐藏对话框,更新过程状态,更新表数据)。
有助于这样做的有用的类是SwingWorker,它的api doc非常广泛,并且有关于如何直接访问模型/组件的示例。
一些伪代码片段,显示如何通过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);
}
}
}
答案 1 :(得分:2)
答案 2 :(得分:0)
我查看了SwingWorker。我完成了这些示例,但我的想法是从SwingWorker中生成每个DataLoader - 我认为。
我决定不这样做,并发现DataLoadDialog类和Swing JDialog之间的交互进展方式存在问题。我太简化了我的例子。还有2个额外的组合也触发了数据加载。一个组合将强制3个数据加载器开始加载,另一个组合用于另外3个数据加载器。这将迫使我添加额外的配置,由于系统的编写方式,这些配置会很尴尬。
我最初尝试将synchronized关键字添加到loadStart和loadComplete,但仍未解决问题。我还在invokeAndWait中显示/隐藏了对话框。我希望这会阻止多个线程尝试显示/隐藏进度 - 但事实并非如此。我用invokeLater替换了invokeAndWait,这似乎可以解决问题。
我完全同意SwingWorker,这样的场景应该在设计中更多地布局 - 我们在这里不好 - 我讨厌。整个并发解决方案在花了大约20分钟同步加载初始数据后放在一起--1个加载器依次从数据库中检索数据。加载的数据量非常可怕 - 而且还必须经过某种调节。
Andez