Better control of what SwingWorker does for you
July 5, 2011
java swing swingworker EDTSwingWorker is a great tool and a part of the standard Swing in JDK now. It does crucial job for any decent Swing application – allows us to execute long running heavy tasks on a separate thread in a background and update all of our UI models and components on UI dispatch thread. Otherwise the only heart and pumping engine of Swing UI, event dispatch thread or EDT, will be busy with moving heavy stuff and poor users won’t get a change to enjoy your wonderful application, starring at frozen pressed menu items or buttons instead.
It’s easy to trust such a great tool and always carelessly call it’s execute() method, not worrying what’s going on behind the scenes, but this is multi-threading and you better control every aspect of it in your application. So, what’s happening when you do something like this?
SwingWorker<String, String> work = new SwingWorker<String, String>() {
@Override
protected String doInBackground() throws Exception {
...
return "";
}
@Override
protected void process(List<String> chunks) {
...
}
@Override
protected void done() {
...
}
work.execute();
When you call execute(), your SwingWorker instance gets executed by itself, and uses internal Executor service, which is fixed size thread pool with 10 threads in current versions of JDK. So if you application is big, and accidentally you have more than 10 background tasks running, all of the subsequent tasks will wait for the next available thread, which may hit the user experience significantly. More than that, recently there was a bug in the JDK regarding SwingWorker not able to execute more than one task at a time.
So looking in the future and expecting your application to grow and even more important, willing to control multi-threading behavior which is crucial to have better user experience with Swing UI applications, it’s better to do the execution of all your background tasks yourself. We just need to create our own Executor service, somehow expose it to all UI modules making use of SwingWorker to do background tasks, and whenever needed, change its implementation or debug it if application shows unsatisfactory behavior running a lot of background tasks.
The easiest way would be providing a service created with a help of a factory, something similar to the following:
public interface BackgroundTaskService {
/**
* Executes SwingWorker task.
* @param worker Worker to be executed.
*/
void execute(SwingWorker<?,?> worker);
/**
* Executes Runnable on background thread, it's up to
* Runnable's code to call invokeLater() to update the UI. Do
* not update or work with UI from the Runnable.
*
* @param work Code to execute on background thread.
*/
void execute(Runnable work);
}
This interface describes the responsibilities of a service, and we can create concrete implementations, for example, based on cached thread pool which has unlimited number of threads, cached as they get created and re-used when they are freed:
public class CachedBackgroundTaskService implements BackgroundTaskService {
private ExecutorService executorService = Executors.newCachedThreadPool();
@Override
public void execute(SwingWorker<?, ?> worker) {
executorService.execute(worker);
}
@Override
public void execute(Runnable work) {
executorService.execute(work);
}
}
We can of course create other implementations, and even test how an application would work without background tasks by creating special implementation which just calls run() method of the SwingWorker or Runnable. That would definitely demonstrate a nightmare of running complex Swing application all in one event dispatch thread.
The different issue is how to provide all of your UI modules with BackgroundTaskService implementation. That’s a well known pattern of Service Locator (e.g.
package com.porty.swing;
import com.porty.swing.service.BackgroundTaskService;
import com.porty.swing.service.CachedBackgroundTaskService;
/**
* Factory which provides access to current implementation of background task service.
* It's instance could be substituted in order to change default library implementation.
*
* @author iportyankin
*/
public class BackgroundTaskServiceFactory {
private static BackgroundTaskServiceFactory instance;
// we use this implementation by default
private CachedBackgroundTaskService taskService = new CachedBackgroundTaskService();
public static BackgroundTaskServiceFactory getInstance() {
if ( instance == null ) {
instance = new BackgroundTaskServiceFactory();
}
return instance;
}
public static void setInstance(BackgroundTaskServiceFactory instance) {
BackgroundTaskServiceFactory.instance = instance;
}
/**
* Obtains currently used implementation of background task service.
* @return Background task service
*/
public BackgroundTaskService getBackgroundTaskService() {
return taskService;
}
}
The other approach would be using some sort of dependency management which is a good idea in general for programming Java Swing UI to be able to alter its behavior quickly. One of the candidates could be Google Guice, easy to use and bind to different implementations.
You can ask what’s the need in that additional abstraction level of background executor if we could just share an instance of ExecutorService between all of the UI modules in the application and make sure they all use it to execute their tasks. The good thing is that we can also provide some additional functionality in the new service related only to UI applications, still keeping the execution done by powerful Java Concurrency library. Most of the time we want the progress to be shown to the user, either indeterminate or determinate, depending on how much information is available to the task. Instead of coding the sequence of showing the progress bars and guarantee to hide them when task is done or failed or cancelled we can introduce a handy calls for that and increase simplicity and elegancy when executing UI tasks.
As an example, let’s start with indeterminate progress message, non-interruptible by the user, a bit simpler than well-defined determinate progress bar, and definitely simpler than interruption-aware automated task support. Let’s add a new method into our background execution interface:
/**
* Executes runnable showing the progress bar with the message until the task is complete.
* @param work Work to be executed.
* @param message Message to be shown
* @param components Components to be disabled while task is executing
*/
void executeIndeterminate(Runnable work, String message, Component... components);
The purpose of this method will be executing the given task in the background thread, under defined execution policy, and while the task is executed, showing the indeterminate progress message of some form, automatically hiding it when the background activity is over. This happens very frequently in Swing/any other single-threaded (or thread-confined) UI toolkit applications. Additionally, we’ll allow client programmer to disable set of components when task starts and enabled them back when it ends – pretty frequent activity as well.
Since the method itself doesn’t define how final task is executed and only builds common infrastructure, we can implement it in a base abstract class, having concrete implementations to only define how to execute tasks and re-use the complex UI-related framework from it.
First, we need to create a component which will show the indeterminate progress bar somehow. Since we don’t know which window or layout to rely on and stay generic, let’s use simplistic blocking modal dialog with an indeterminate progress bar and a label with the message. It’s part of the Swing Tools project and located in com.porty.swing.dialog package.
/**
* Abstract implementation of the background service with some UI work prepared.
* Has no actual execution code.
*
* @author iportyankin
*/
public abstract class AbstractBackgroundTaskService implements BackgroundTaskService {
protected Component createProgressComponent(String message) {
BlockingProgressDialog dialog = new BlockingProgressDialog(null, message);
dialog.setVisible(true);
WindowUtils.centerWindow(dialog);
return dialog;
}
protected void destroyProgressComponent(Component c) {
((BlockingProgressDialog) c).dispose();
}
}
These two methods are protected so you can override them and provide progress implementation more elegant and suitable for your application needs.
Then we need to wrap the background call with UI setup and tear down code and execute the final sequence. It sounds simple, i.e. create and show component before running task and dispose it after it’s done. Naive implementation could look like that:
@Override
public void executeIndeterminate(final Runnable work, final String message, final Component... components) {
final List<Component> uiProgressComponent = new ArrayList<Component>(1);
// disable the components
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (null != components) {
// disable components
for (Component next : components) {
next.setEnabled(false);
}
// create a progress component
uiProgressComponent.add(createProgressComponent(message));
}
}
});
Runnable proxy = new Runnable() {
@Override
public void run() {
try {
work.run();
} finally {
// enable the components back
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (null != components) {
// disable components
for (Component next : components) {
next.setEnabled(true);
}
}
// dispose progress component
destroyProgressComponent(uiProgressComponent.get(0));
}
});
}
}
};
// execute proxy with actual work
execute(proxy);
}
What we do here is schedule UI activities to be run later when all pending events are processed, including disabling the components and creating generic progress component, and then immediately run the task itself (using execute() method which will be defined by concrete execution strategy). To be able to catch end of the task, regardless of anything that happens with it, we create a proxy Runnable on top of it and use finally section to do post-mortem activities, again scheduling UI task for later, to re-enable the components and destroy progress bar component. We don’t need any sophisticated concurrency tools here since UI thread and invokeLater() guarantee that first UI task is executed before the second one, so the component will be created and available in the exchange list (we have to use final list – restriction of inner classes access to outer variables).
The beauty of the new method is extreme simplicity and handling of many activities behind the scenes for you – to execute some work with progress and disabling a button while it’s executing is one line of code:
BackgroundTaskServiceFactory.getInstance().
getBackgroundTaskService().executeIndeterminate(worker, "Working slow...", b);
You can see some of the Swing FEST automated tests in test package for the services for standard UI.
Moving forward, it makes sense to add determinate progress with a callback which allows updating the progress, and cancellable implementation which honors the interruption policy and supplies a standard interrupt button in the UI which does it.
Remember, you can find all of the articles code and tools, as well as latest additions and changes at project code site https://github.com/ivanporty/swingtools