Let's start with a task that is very simple, but potentially time-consuming. Theapplet loads a set of graphic files used in an animation. If the graphic files are loaded from an initial thread, there may be a delay before the GUI appears. If the graphic files are loaded from the event dispatch thread, the GUI may be temporarily unresponsive.
TumbleItem
To avoid these problems,
TumbleItem
creates and executes an instance ofSwingWorker
from its initial threads. The object'sdoInBackground
method, executing in a worker thread, loads the images into anImageIcon
array, and returns a reference to it. Then thedone
method, executing in the event dispatch thread, invokesget
to retrieve this reference, which it assigns to an applet class field namedimgs
. This allowsTumbleItem
to construct the GUI immediately, without waiting for the images to finish loading.Here is the code that defines and executes the
SwingWorker
object.All concrete subclasses ofSwingWorker worker = new SwingWorker<ImageIcon[], Void>() { @Override public ImageIcon[] doInBackground() { final ImageIcon[] innerImgs = new ImageIcon[nimgs]; for (int i = 0; i < nimgs; i++) { innerImgs[i] = loadImage(i+1); } return innerImgs; } @Override public void done() { //Remove the "Loading images" label. animator.removeAll(); loopslot = -1; try { imgs = get(); } catch (InterruptedException ignore) {} catch (java.util.concurrent.ExecutionException e) { String why = null; Throwable cause = e.getCause(); if (cause != null) { why = cause.getMessage(); } else { why = e.getMessage(); } System.err.println("Error retrieving file: " + why); } } };SwingWorker
implementdoInBackground
; implementation ofdone
is optional.Notice that
SwingWorker
is a generic class, with two type parameters. The first type parameter specifies a return type fordoInBackground
, and also for theget
method, which is invoked by other threads to retrieve the object returned bydoInBackground
.SwingWorker
's second type parameter specifies a type for interim results returned while the background task is still active. Since this example doesn't return interim results,Void
is used as a placeholder.You may wonder if the code that sets
imgs
is unnecessarily complicated. Why makedoInBackground
return an object and usedone
to retrieve it? Why not just havedoInBackground
setimgs
directly? The problem is that the objectimgs
refers to is created in the worker thread and used in the event dispatch thread. When objects are shared between threads in this way, you must make sure that changes made in one thread are visible to the other. Usingget
guarantees this, because usingget
creates a happens before relationship between the code that createsimgs
and the code that uses it. For more on the happens before relationship, refer to Memory Consistency Errors in the Concurrency lesson.There are actually two ways to retrieve the object returned by
doInBackground
.Be careful when invoking either overload of
- Invoke
SwingWorker.get
with no arguments. If the background task is not finished,get
blocks until it is.- Invoke
SwingWorker.get
with arguments indicating a timeout. If the background task is not finished,get
blocks until it is — unless the timeout expires first, in which caseget
throwsjava.util.concurrent.TimeoutException
.get
from the event dispatch thread; untilget
returns, no GUI events are being processed, and the GUI is "frozen". Don't invokeget
without arguments unless you are confident that the background task is complete or close to completion.For more on the
TumbleItem
example, refer to How to Use Swing Timers in the lesson Using Other Swing Features.