SwingWorker to update TreeModel?
I have a lot of JTree in my application, all of which use different instances (with different parameters) of the same custom TreeModel class. I am trying to add some threads to speed up the process, since the getChildren () member takes a while to start, I added a SwingWorker subclass to the TreeModel and in my getChildren () instantiate this worker and execute it, then return the get () result.
I keep getting ConcurrentModificationExceptions and I know I have to synchronize something, but I don't know what. Has anyone got any suggestions on how to safely keep the TreeModel responsive?
Thanks! Joshua
First, I'm wondering why you are trying to use streams to speed things up. Is it CPU bound or network / disk bound?
Your description will look like yours is getChildren
waiting for the operation to complete before returning. This can be useful for parallel operation on multi-core hardware, but is not a general model. Typically what we (and what we do SwingWorker
) is to run the code for the blocks on a different thread, while the Dispatch Event thread can continue until it receives an event to process data from the background thread.
SwingWorker
doesn't document what it throws ConcurrentModificationException
anywhere. A stack trace can be helpful.
I generally suggest avoiding SwingWorker
. This is great for making a small demo operation, but it creates a poor design.
Threading is difficult, especially now that multi-threaded hardware actually has more bugs. Liberal spraying synchronized
will not cause problems. A relatively simple general approach is to avoid sharing mutable objects (or at least mutating shared objects). Pass a set of arguments to the background thread that are not used in EDT. Likewise, pass arguments back to EDT that are not otherwise modified.
a source to share
ConcurrentModificationExceptions happen when you access the same list across two threads. Your TreeModel probably uses ArrayLists, Vectors, Hashtables or something similar to store nodes.
I'm guessing one of two things are happening:
1) Your TreeModel is constantly requested by the JTree on the EDT stream every time it is displayed. This is inevitable and how Swing works. If you are accessing the tree model or underlying lists on a different thread, you will sometimes do so at the same time as the render and List throws an exception.
2) you have two working swings, each on its own threads. They update / query the list at the same time. Same problem as above.
I agree with Tom that using SwingWorker to make your TreeModel "asynchronous" is a very difficult problem to be avoided. But to answer your question, I would look at the following piece of code. Notice how all the query and update actions for the tree take place in the "finished" method, which always runs on the EDT. I am assuming you are doing the get / sets in the construct () method.
public TreeNode[] getChildren(TreeNode parent){
// Do all normal work you would do
...
// figure out if this parents children have not been fetched yet
boolean needToFetch = ....
if(needToFetch){
worker = new SwingWorker() {
public Object construct() {
// go fetch your children from whatever database, server, file...
// fetchNewChildren must NOT access the TreeModel or any of the underlying
// lists, because you will get a modification exception. This is what
// Tom meant by "Pass a set of arguments into the background thread
// that are not being used in the EDT"
return fetchNewChildNodes(parent);
}
public void finished() {
List<TreeNode> newNodes = (List<TreeNode>)get();
// insert the newly fetched nodes into the parent
// if done correclty, this will fire TreeModelChanged events
// so the tree should re-render, repaint, etc...
parent.addChildren(newNodes);
}
};
worker.start();
}
a source to share