views:

62

answers:

2

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 solution to me.

+2  A: 

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.

Peter Tillemans
+4  A: 

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.

Noel Ang