The JavaTM Tutorial
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search

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 if 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(in the Creating a User Interface trail) and Threads and Event Handling(in the Creating a User Interface trail).

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:

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 Timer. 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. A Timer object implements a thread that spawns one or more action events after a specified delay. If you need to implement your own threads, you can find information on doing so in Doing Two or More Tasks At Once: Threads(in the Learning the Java Language trail).

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

The rest of this section discusses SwingWorker and the SwingUtilities invoke methods. For information and examples of using timers, see How to Use Timers.

Using the SwingWorker Class


Note:  The tutorial's implementation of the SwingWorker class was updated in the January 1999 release of the tutorial; the class has been enhanced to allow programs to safely interrupt the thread. If you are using SwingWorker in any of your programs, make sure you are using the latest version!
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 that 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;
        }
    };
    ...
}

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);
        }
    };
}

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 Swing Connection articles Threads and Swing and Using a SwingWorker Thread.
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search