我刚刚发现了一个有趣的情况。假设你有一些SwingWorker(我已经让这个模糊地让人联想到我自己):
public class AddressTreeBuildingWorker extends SwingWorker<Void, NodePair> {
private DefaultTreeModel model;
public AddressTreeBuildingWorker(DefaultTreeModel model) {
}
@Override
protected Void doInBackground() {
// Omitted; performs variable processing to build a tree of address nodes.
}
@Override
protected void process(List<NodePair> chunks) {
for (NodePair pair : chunks) {
// Actually the real thing inserts in order.
model.insertNodeInto(parent, child, parent.getChildCount());
}
}
private static class NodePair {
private final DefaultMutableTreeNode parent;
private final DefaultMutableTreeNode child;
private NodePair(DefaultMutableTreeNode parent, DefaultMutableTreeNode child) {
this.parent = parent;
this.child = child;
}
}
}
如果在后台完成的工作很重要,那么事情就会运作良好 - 使用相对较小的对象列表调用process()
,一切都很愉快。
问题是,如果在后台完成的工作由于某种原因突然变得微不足道,process()
会收到大量的对象(例如,我看过1,000,000个),当你处理每个对象时,你在事件调度线程上花费了20秒,这正是SwingWorker旨在避免的。
如果不清楚,这两个都出现在同一个SwingWorker类中 - 它取决于输入数据和调用者想要的处理类型。
有没有正确的方法来处理这个问题?显然我可以故意延迟或产生后台处理线程,以便每次都可以使用较小的数字 ,但这对我来说不是正确的解决方案。
答案 0 :(得分:4)
您可以尝试发布较小的结果块。
如果这没有帮助,您可能还会考虑限制UI更新而不是计算。您可以通过process
将收到的NodePairs存储到阻塞队列来缓和UI更新:
@Override
protected Void doInBackground() {
this.treeModelUpdater.execute();
// Omitted; performs variable processing to build a tree of address nodes.
}
@Override
protected void process(List<NodePair> chunks) {
this.queue.addAll( chunks ); // a blocking queue
}
@Override
protected void done() {
// Null Object as sentinel value to unblock TreeModelUpdater
// and signal it to end doInBackground.
this.queue.put( new NullNodePair() );
}
TreeModelUpdater(this.treeModelUpdater
)将成为从属SwingWorker实现,其 doInBackground
方法以限制方式从队列中检索并发布NodePairs。
答案 1 :(得分:2)
据我所知,流程方法主要是用后台处理结果更新UI。在1Mio chunk的情况下,它让我想知道所有信息将在屏幕上显示在哪里? :-)
我会在一个相当大的列表中将这些块一起批处理并将其发送以进行处理,以便至少显着减少同步部分。
如果您需要能够使用控件显示1Mio数据元素,那么性能将受到影响,除非您使用延迟表示技术,即仅实例化实际可见的内容。在这种情况下,处理例程将不再过载。