The JavaTM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Trail: Creating a GUI with JFC/Swing
Lesson: Using Other Swing Features

## How to Use Threads

> The first rule of using threads is this: avoid them when you can. Threads can be difficult to use, and they tend to make programs harder to debug. To avoid the possibility of deadlock, you must take extreme care that any threads you create don't invoke any methods on Swing components. Remember, once a Swing component has been realized, only the event-dispatching thread should affect or query the component. If you aren't familiar with the role of the event-dispatching thread, please read Threads and Swing and Threads and Event Handling.

> Despite the dangers, threads can be invaluable. You can use them to improve your program's perceived performance. And sometimes threads can simplify a program's code or architecture. Here are some typical situations where threads are used:

> - To move a time-consuming initialization task out of the main thread, so that the GUI comes up faster. Examples of time-consuming tasks include making extensive calculations and blocking for network or disk I/O (loading images, for example).

> - To move a time-consuming task out of the event-dispatching thread, so that the GUI remains responsive.

> - To perform an operation repeatedly, usually with some predetermined period of time between operations.

> - To wait for messages from other programs.

> If you need to create a thread, you can avoid some common pitfalls by implementing the thread with a utility class such as `SwingWorker` or one of the `Timer` classes. A `SwingWorker` object creates a thread to execute a time-consuming operation. After the operation is finished, `SwingWorker` gives you the option of executing some additional code in the event-dispatching thread. Timers are useful for performing a task either repeatedly or after a specified delay. If you need to implement your own threads, you can find information on doing so in Threads: Doing Two or More Tasks At Once.

> You can use several techniques to make multi-threaded Swing programs work well:

> - If you need to update a component but your code isn't executing in an event handler, use one of these two `SwingUtilities` methods: `invokeLater` (preferred) or `invokeAndWait`. These methods let you specify that some code be executed in the event-dispatching thread.

> - If you aren't sure whether your code is executing in an event handler, then you should analyze your program's code and document which thread each method is (and can be) called from. Failing that, you can use the `SwingUtilities.isEventDispatchThread()` method, which returns true if your code is executing in the event-dispatching thread. You can safely call `invokeLater` from any thread, but `invokeAndWait` throws an exception if it's called from the event-dispatching thread.

> - If you need to update a component after a delay (whether or not your code is currently executing in an event handler), use a timer to do so.

> - If you need to update a component at a regular interval, use a timer.
> The rest of this section discusses `SwingWorker` and the `SwingUtilities` invoke methods. For information and examples of using timers, see How to Use Swing Timers.

### Using the SwingWorker Class

> > Note: The implementation of the `SwingWorker` class has been updated twice, most recently in February 2000. The first update (in January 1999) allowed programs to safely interrupt the worker thread. The most recent update (called "SwingWorker 3") was to fix a subtle threading bug that could cause a `NullPointerException`.

> The `SwingWorker` class is implemented in `SwingWorker.java`, which is not in the Swing release. To use the `SwingWorker` class, you first create a subclass of it. The subclass must implement the `construct` method so that it contains the code to perform your lengthy operation. When you instantiate your `SwingWorker` subclass, the `SwingWorker` creates a thread but does not (as of SwingWorker 3) start it. You then invoke `start` on your `SwingWorker` object to start the thread, which then calls your `construct` method.

> Here is an example of using a `SwingWorker` to move a time-consuming task from an action event handler into a background thread, so that the GUI remains responsive.

> >     //OLD CODE:
> >     public void actionPerformed(ActionEvent e) {
> >         ...
> >         //...code that might take a while to execute is here...
> >         ...
> >     }

> >     //BETTER CODE:
> >     public void actionPerformed(ActionEvent e) {
> >         ...
> >         final SwingWorker worker = new SwingWorker() {
> >             public Object construct() {
> >                 //...code that might take a while to execute is here...
> >                 return someValue;
> >             }
> >         };
> >         worker.start();  //required for SwingWorker 3
> >         ...
> >     }

> The value that `construct` returns can be any object. If you need to get the value, you can do so by invoking the `get` method on your `SwingWorker` object. Be careful about using `get`. Because it blocks, it can cause deadlock. If necessary, you can interrupt the thread (causing `get` to return) by invoking `interrupt` on the `SwingWorker`.

> If you need to detect when the time-consuming operation completes, you can do so either by using `get` (which is dangerous, as we noted) or by overriding the `finished` method in your `SwingWorker` subclass. The `finished` method runs after the `construct` method returns. Because the `finished` method executes in the event-dispatching thread, you can safely use it to update Swing components. Of course, you shouldn't put time-consuming operations in your `finished` implementation.

> The following example of implementing `finished` is taken from `IconDemoApplet.java`. For a full discussion of this applet, including how it improves perceived performance by using background threads to load images, see How to Use Icons.

> >     public void actionPerformed(ActionEvent e) {
> >         ...
> >         if (icon == null) {     //haven't viewed this photo before
> >             loadImage(imagedir + pic.filename, current);
> >         } else {
> >             updatePhotograph(current, pic);
> >         }
> >     }
> >     ...
> >     //Load an image in a separate thread.
> >     private void loadImage(final String imagePath, final int index) {
> >         final SwingWorker worker = new SwingWorker() {
> >             ImageIcon icon = null;

> >             public Object construct() {
> >                 icon = new ImageIcon(getURL(imagePath));
> >                 return icon; //return value not used by this program
> >             }

> >             //Runs on the event-dispatching thread.
> >             public void finished() {
> >                 Photo pic = (Photo)pictures.elementAt(index);
> >                 pic.setIcon(icon);
> >                 if (index == current)
> >                     updatePhotograph(index, pic);
> >             }
> >         };
> >         worker.start();  //required for SwingWorker 3
> >     }

> For more examples of using `SwingWorker`, go to How to Monitor Progress. Also, `TumbleItem.java`, which is discussed in How to Make Applets, uses both a `SwingWorker` and a `Timer`.

### Using the invokeLater Method

> You can call `invokeLater` from any thread to request the event-dispatching thread to run certain code. You must put this code in the `run` method of a `Runnable` object and specify the `Runnable` object as the argument to `invokeLater`. The `invokeLater` method returns immediately, without waiting for the event-dispatching thread to execute the code. Here's an example of using `invokeLater`:

> >     Runnable updateAComponent = new Runnable() {
> >         public void run() { component.doSomething(); }
> >     };
> >     SwingUtilities.invokeLater(updateAComponent);

### Using the invokeAndWait Method

> The `invokeAndWait` method is just like `invokeLater`, except that `invokeAndWait` doesn't return until the event-dispatching thread has executed the specified code. Whenever possible, you should use `invokeLater` instead of `invokeAndWait`. If you use `invokeAndWait`, make sure that the thread that calls `invokeAndWait` does not hold any locks that other threads might need while the call is occurring.

> Here's an example of using `invokeAndWait`:

> >     void showHelloThereDialog() throws Exception {
> >         Runnable showModalDialog = new Runnable() {
> >             public void run() {
> >                 JOptionPane.showMessageDialog(myMainFrame,
> >                                               "Hello There");
> >             }
> >         };
> >         SwingUtilities.invokeAndWait(showModalDialog);
> >     }

> Similarly, a thread that needs access to GUI state, such as the contents of a pair of text fields, might have the following code:

> >     void printTextField() throws Exception {
> >         final String[] myStrings = new String[2];

> >         Runnable getTextFieldText = new Runnable() {
> >             public void run() {
> >                 myStrings[0] = textField0.getText();
> >                 myStrings[1] = textField1.getText();
> >             }
> >         };
> >         SwingUtilities.invokeAndWait(getTextFieldText);

> >         System.out.println(myStrings[0] + " " + myStrings[1]);
> >     }

> For more examples of using the invoke methods, see the BINGO example, especially the following classes: `CardWindow`, `ControlPane`, `Player`, and `OverallStatusPane`.

For more information about Swing thread issues, see the article index in The Swing Connection.

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Copyright 1995-2001 Sun Microsystems, Inc. All rights reserved.