Quantcast
Channel: Scratch Where It's Itching
Viewing all articles
Browse latest Browse all 73

SwingWorker is not a Thread

$
0
0
One practical class when developping Swing application is the SwingWorker. It allows you to sart a lengthy task in the background, and then applying the result to the display on the Event Thread. The usual pattern is the following:
   class MySwingWorker extends SwingWorker {       @Override       public Void doInBackground() {           //perform background work       }        @Override       protected void done() {          //update display       }   }

While the background work might be long, it is possible also to update the user interface with partial results. In which case, the pattern is the following:

   class MySwingWorker extends SwingWorker, Integer> {       @Override       public Void doInBackground() {           while (!finished) {             //perform background work             publish(partialResult)           }           return result;       }        @Override       protected void process(List chunks) {          //update display with partial results       }   }

This seemed so pratical for me, that I decided to use this instead of one of our Thread that is running in the background, waiting for some data, processing it, then adding one or several lines to a JTable. The idea would be to have my SwingWorker running constantly in the background, waiting for data from a blocking queue, then publishing the result to the Swing updating part. It looked like this:

class MyEternalSwingWorker extends SwingWorker {       @Override       public Void doInBackground() {           while (!isCancelled()) {             queue.take();             //perform background work             publish(row)           }           return null;       }        @Override       protected void process(List chunks) {          //add rows to the JTable       }   }

If data were to arrive too fast, Swing has the possibility to coalesce all the updates, which should bring better performance. It work quite fine, but suddenly, some of the users started to have some strange behaviors, and updates stopped quite unexpectedly for some windows. As it appears, it depends on the number of windows opened, and so on the number of such SwingWorkers running. That is when I discovered this interesting line in the SwingWorker code:

    private static final int MAX_WORKER_THREADS = 10;

SwingWorker is using a Thread Pool, with 10 Threads, and there is no way to change that. So all my nice Eternal Swing Workers were just using all the available Threads in the Pool, and no other Worker could start their background work.

So beware, SwingWorkers can not be used as Threads. They need to be used for finite work. Or be less than 10.

Update: As Eugene Ho suggested in the comments, SwingWorker is actually a Runnable. So you can run it in your own Thread without blocking anything in the SwingWorker's ThreadPool, while keeping all the advantages, such as events coalescing, of the class.

Update 2: Also, the number of Threads in the pool is a constant, but it is possible to override it with your own Thread Pool, since SwingWorker use the sun.awt.AppContext class to fetch it. Do the following:

  sun.awt.AppContext.getAppContext().put(SwingWorker.class, myThreadPoolExecutor);


Viewing all articles
Browse latest Browse all 73

Trending Articles