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.TumbleItemTo avoid these problems,
TumbleItemcreates and executes an instance ofSwingWorkerfrom its initial threads. The object'sdoInBackgroundmethod, executing in a worker thread, loads the images into anImageIconarray, and returns a reference to it. Then thedonemethod, executing in the event dispatch thread, invokesgetto retrieve this reference, which it assigns to to an applet class field namedimgsThis allowsTumbleItemto contruct the GUI immediately, without waiting for the images to finish loading.Here is the code that defines and executes the
SwingWorkerobject.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); } } };SwingWorkerimplementdoInBackground; implementation ofdoneis optional.Notice that
SwingWorkeris a generic class, with two type parameters. The first type parameter specifies a return type fordoInBackground, and also for thegetmethod, 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,Voidis used as a placeholder.You may wonder if the code that sets
imgsis unnecessarily complicated. Why makedoInBackgroundreturn an object and usedoneto retrieve it? Why not just havedoInBackgroundsetimgsdirectly? The problem is that the objectimgsrefers 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. Usinggetguarantees this, because usinggetcreates a happens before relationship between the code that createsimgsand 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.getwith no arguments. If the background task is not finished,getblocks until it is.- Invoke
SwingWorker.getwith arguments indicating a timeout. If the background task is not finished,getblocks until it is — unless the timeout expires first, in which casegetthrowsjava.util.concurrent.TimeoutException.getfrom the event dispatch thread; untilgetreturns, no GUI events are being processed, and the GUI is "frozen". Don't invokegetwithout arguments unless you are confident that the background task is complete or close to completion.For more on the
TumbleItemexample, refer to How to Use Swing Timers in the lesson Using Other Swing Features.