What if a large number of objects are passed to my SwingWorker.process() method?
I just found an interesting situation. Suppose you have some SwingWorker (I've made this one vaguely reminiscent of my own):
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;
}
}
}
If the work done in the background is significant then things work well - process()
is called with relatively small lists of objects and everything is happy.
Problem is, if the work done in the background is suddenly insignificant for whatever reason, process()
receives a huge list of objects (I have seen 1,000,000, for instance) and by the time you process each object, you have spent 20 seconds on the Event Dispatch Thread, exactly what SwingWorker was designed to avoid.
In case it isn't clear, both of these occur on the same SwingWorker class for me - it depends on the input data, and the type of processing the caller wanted.
Is there a proper way to handle this? Obviously I can intentionally delay or yield the background processing thread so that a smaller number might arrive each time, but this doesn't feel like the right solutio开发者_如何学编程n to me.
You can try publishing smaller chunks of the results.
If that does not help, you might also consider throttling the UI updates instead of the computation. You could moderate the UI update by having process
store the NodePairs it receives into a blocking queue:
@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
) would then be a subordinate SwingWorker implementation whose doInBackground
method retrieves and publishes the NodePairs from the queue in a throttled manner.
As far as I know the process method is primarily to update the UI with the results of background processing. In the case of 1Mio chunk it makes me wonder where all that info would go on the screen? :-)
I would batch the chunks together in a sizable List and send that over for processing so that at least the synchronisation part is reduced significantly.
If you need to be able to display 1Mio dataelements using controls then performance is going to suffer anyway, unless you use lazy representation techniques, i.e. only instantiate the stuff which is actually visible. In this case the process routine will no longer be overloaded.
精彩评论