SwingCommand part 4 – Using Executors
SwingCommand has good integration with the java.util.Concurrent, and this allows you to control which background thread will be used to run your BackgroundTask. You can specify the Executor which should be used by your SwingCommand.
This can be very useful. For example, there are occasions when it is important to ensure that only one background task of a certain type runs at a time. You could achieve this in the UI by disabling buttons or menu items while a command executes – but if your SwingCommand instance is shared and used in many areas of your application it might be nice to provide a firmer guarantee.
To achieve this, you could associate a single thread Executor with a SwingCommand. If the SwingCommand is executed again before the initial task has finished running, the second Task will then be queued to start processing once the first has completed. (Sometimes it is also a good idea to cancel the currently processing task at this point.)
//we can associate a single thread executor -
command.setExecutor(Executors.newSingleThreadExecutor());
Using a Thread Pool
Alternatively, let’s pretend there are many different commands in our app, and our users hammer buttons as if they were being paid per click. We don’t want to risk spamming the database server with requests (it’s probably on its last legs anyway!). A good solution might be to impose a limit on the number of threads which can concurrently run background tasks in our application. Once these threads are active, any new Tasks will be queued until there is a spare thread.
We could do this very easily by specifying a fixed size thread pool for all our SwingCommands. For example, the following code snippit creates a thread pool limited to three threads, and associates it with a command.
Executor e = Executors.newFixedThreadPool(3);
command.setExecutor(e);
To make sure all our commands use the thread pool, we could subclass SwingCommand to supply the thread pool Executor as the default. We could then use ThreadPoolSwingCommand instead of the basic SwingCommand throughout out application.
//Our SwingCommand classes should all subclass this instead
static abstract class ThreadPoolSwingCommand<P> extends SwingCommand<P> {
private static Executor threadPoolExecutor = Executors.newFixedThreadPool(3);
protected Executor getDefaultBackgroundTaskExecutor() {
return threadPoolExecutor;
}
}
Setting one-off Executors
Similar to TaskListeners, you can also specify an Executor to be used for a one-off invocation of a SwingCommand. You do this by passing an Executor into the execute() method.
SwingCommand<String> c = new LoadMessagesCommand("My User");
c.execute(Executors.newSingleThreadExecutor());
Processing a SwingCommand synchronously on a background thread
Passing in an Executor to the execute() method in this way can change the way in which a SwingCommand is processed. Usually, a SwingCommand launched from a background thread is processed asynchronously, so as not to block the thread which calls execute(). However, in some cases you may want the current background thread to be used for the execution of the Task – in which case the call to command.execute() will not return until the Task has finished processing. In this case, you can pass in a simple Executor which runs the command on the current thread:
SwingCommand<String> c = new LoadMessagesCommand("My User");
c.execute(new Executor() {
public void execute(Runnable command) {
//just process synchronously in the current thread
command.run();
}
});
In the above example the normal threading guarantees for the Task are still obeyed – doInEventThread() will still be run in the Swing Event Thread. The background thread will be blocked while the EventThread does its work.
It should be clear from the above that the ability to set the Executor for a SwingCommand or a Task gives you a lot of control over the threads used to process your SwingCommands.
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Leave a Reply