Previous | Next | Trail Map | Creating a User Interface | Using the JFC/Swing Packages

Threads and Swing


Note: This section is a simplified version of an article in The Swing Connection. For more information about Swing thread issues, see the article: Threads and Swing.

This section tells you how to use the Swing API in a thread-safe way. If your program is an application (not an applet) and has the following common pattern, then you don't need to worry about thread safety:

//Thread-safe example
public class MyApplication {
    public static void main(String[] args) {
	JFrame f = new JFrame(...);
        ...//Add components to the frame here...
	f.pack();
	f.setVisible(true);
	//Don't do any more GUI work here.
    }

    ...
    //All manipulation of the GUI -- setText, getText, etc. --
    //is performed in event handlers such as actionPerformed().
    ...
}
However, if your program is an applet, creates threads to perform tasks that affect the GUI, or manipulates the already-visible GUI in response to anything but an AWT event, then read on! Four subsections follow; please read at least the first two.

The Single-Thread Rule
Swing components can be accessed by only one thread at a time. Generally, this thread is the event-dispatching thread.
Exceptions to the Rule
A few operations are guaranteed to be thread-safe.
How to Execute Code in the Event-Dispatching Thread
If you need access to the UI from outside event-handling or drawing code, then you can use the SwingUtilities invokeLater or invokeAndWait method.
How to Create Threads
If you need to create a thread -- for example, to handle a job that's computationally expensive or I/O bound -- you can use a thread utility class such as SwingWorker or Timer.

The Single-Thread Rule

The single-thread rule is as follows:


Rule: Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.

This rule might sound scary, but for many simple programs, you don't have to worry about threads. Before we go into detail about how to write Swing code, let's define two terms: realized and event-dispatching thread.

Realized means that the component's paint method has been or might be called. A Swing component that's a top-level window is realized by having one of these methods invoked on it: setVisible(true), show, or (this might surprise you) pack. Once a window is realized, all components that it contains are realized. Another way to realize a component is to add it to a container that's already realized. You'll see examples of realizing components later.

The event-dispatching thread is the thread that executes drawing and event-handling code. For example, the paint and actionPerformed methods are automatically executed in the event-dispatching thread. Another way to execute code in the event-dispatching thread is to use the SwingUtilities invokeLater method.

Exceptions to the Rule

There are a few exceptions to the rule that all code that might affect a realized Swing component must run in the event-dispatching thread:

A few methods are thread-safe.
In the Swing API documentation, thread-safe methods are marked with this text:
This method is thread safe, although most Swing methods are not. Please see Threads and Swing for more information.
An application's GUI can often be constructed and shown in the main thread.
As long as no components (Swing or otherwise) have been realized in the current runtime environment, it's fine to construct and show a GUI in the main thread of an application. To help you see why, here's an analysis of the thread safety of the thread-safe example. To refresh your memory, here are the important lines from the example:
public static void main(String[] args) {
    JFrame f = new JFrame(...);
    ...//Add components to the frame here...
    f.pack();
    f.setVisible(true);
    //Don't do any more GUI work here.
}
  1. The example constructs the GUI in the main thread. In general, you can construct (but not show) a GUI in any thread, as long as you don't make any calls that refer to or affect already-realized components.
  2. The components in the GUI are realized by the pack call.
  3. Immediately afterward, the components in the GUI are shown with the setVisible (or show) call. Technically, the setVisible call is unsafe because the components have already been realized by the pack call. However, because the program doesn't already have a visible GUI, it's exceedingly unlikely that a paint call will occur before setVisible returns.
  4. The main thread executes no GUI code after the setVisible call. This means that all GUI work moves from the main thread to the event-dispatching thread, and the example is, in practice, thread-safe.

The following JComponent(in the API reference documentation) methods are safe to call from any thread: repaint, revalidate, and invalidate.
The repaint and revalidate methods enqueue requests for the event-dispatching thread to call paint and validate, respectively. The invalidate method just marks a component and all of its direct ancestors as requiring validation.

Listener lists can be modified from any thread.
It's always safe to call the addListenerTypeListener and removeListenerTypeListener methods. The add/remove operations have no effect on an event dispatch that's under way.

How to Execute Code in the Event-Dispatching Thread

Most post-initialization GUI work naturally occurs in the event-dispatching thread. Once the GUI is visible, most programs are driven by events such as button actions or mouse clicks, which are always handled in the event-dispatching thread.

However, some programs need to perform non-event-driven GUI work after the GUI is visible. Here are some examples:

Applets:
Because every applet is added to an already visible page, the code that adds components to the applet should be executed in the event-dispatching thread. [PENDING: link to JApplet page]

Programs that must perform a lengthy initialization operation before they can be used:
This kind of program should generally show some GUI while the initialization is occurring, and then update or change the GUI. The initialization should not occur in the event-dispatching thread; otherwise, repainting and event dispatch would stop. However, after initialization the GUI update/change should occur in the event-dispatching thread, for thread-safety reasons.

Programs whose GUI must be updated as the result of non-AWT events:
For example, suppose a server program can get requests from other programs that might be running on different machines. These requests can come at any time, and they result in one of the server's methods being invoked in some possibly unknown thread. How can that method update the GUI? By executing the GUI update code in the event-dispatching thread.

The SwingUtilities class provides two methods to help you run code in the event-dispatching thread:

invokeLater: Requests that some code be executed in the event-dispatching thread. This method returns immediately, without waiting for the code to execute.

invokeAndWait: Acts like invokeLater, except that this method waits for the code to execute. As a rule, you should use invokeLater instead of this method.

This page gives you some examples of using this API. Also see the BINGO example, especially the following classes: CardWindow, ControlPane, Player, and OverallStatusPane.


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 doWorkRunnable = new Runnable() {
    public void run() { doWork(); }
};
SwingUtilities.invokeLater(doWorkRunnable);

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

How to Create Threads

If you can get away with it, avoid using threads. Threads can be difficult to use, and they make programs harder to debug. In general, they just aren't necessary for strictly GUI work, such as updating component properties.

However, sometimes threads are necessary. Here are some typical situations where threads are used:

You can use two classes to help you implement threads:

Using the SwingWorker Class

The SwingWorker class is implemented in SwingWorker.java, which is not in the Swing release. SwingWorker does all the dirty work of implementing a background thread. Although many programs don't need background threads, background threads are sometimes useful for performing time-consuming operations, which can improve the perceived performance of a program.

To use the SwingWorker class, you first create a subclass of it. In the subclass, you 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. When you need the object returned by the construct method, you call the SwingWorker's get method. Here's an example of using SwingWorker:

...//in the main method:
    final SwingWorker worker = new SwingWorker() {
        public Object construct() {
            return new ExpensiveDialogComponent();
        }
    };

...//in an action event handler:
    JOptionPane.showMessageDialog
        (f, worker.get());

When the program's main method creates the SwingWorker object, the SwingWorker immediately starts a new thread that instantiates ExpensiveDialogComponent. The main method also constructs a GUI that consists of a window with a button.

When the user clicks the button, the program blocks, if necessary, until the ExpensiveDialogComponent has been created. The program then shows a modal dialog containing the ExpensiveDialogComponent. You can find the entire program in PasswordDemo.java. Also, the example program provided in How to Use Progress Bars runs a long task in a SwingWorker thread.


Previous | Next | Trail Map | Creating a User Interface | Using the JFC/Swing Packages