全部,
我们在应用程序中加载数据时遇到问题。当前实现的目的是为数据加载提供异步机制。
我基本上想知道是否有任何好的模式或实践。
该应用程序有许多不同的 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 有一个对每个数据加载器的引用。每个数据加载器都有一个状态,它通过一个 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/