Keeping Swing thread safe
The last step in creating a Swing GUI is to start it up. The correct way to start up a Swing GUI today differs from Sun's originally prescribed approach. Here's the quote from the Sun documentation again:
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.
Now throw those instructions out the window, because around when JSE 1.5 was released all the examples on Sun's site changed. Since that time it has been a little-known fact that you are supposed to always access Swing components on the event-dispatch thread to ensure their thread safety/single-threaded access. The reason behind the change is simple: while your program might access a Swing component off of the event-dispatch thread before the component is realized, the initialization of the Swing UI could trigger something to run on the event-dispatch thread afterward, because the component/UI expects to run everything on the event-dispatch thread. Having GUI components run on different threads breaks Swing's single-threaded programming model.
The program in Listing 5 isn't quite realistic, but it serves to make my point.
Listing 5. Accessing Swing component state from multiple threads
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BadSwingButton {
public static void main(String args[]) {
JFrame frame = new JFrame("Title");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Press Here");
ContainerListener container = new ContainerAdapter() {
public void componentAdded(final ContainerEvent e) {
SwingWorker worker = new SwingWorker() {
protected String doInBackground() throws InterruptedException {
Thread.sleep(250);
return null;
}
protected void done() {
System.out.println("On the event thread? : " +
EventQueue.isDispatchThread());
JButton button = (JButton)e.getChild();
String label = button.getText();
button.setText(label + "0");
}
};
worker.execute();
}
};
frame.getContentPane().addContainerListener(container);
frame.add(button, BorderLayout.CENTER);
frame.setSize(200, 200);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
System.out.println("I'm about to be realized: " +
EventQueue.isDispatchThread());
frame.setVisible(true);
}
}
Notice that the output shows some code running on the main thread prior to the UI being realized. This means that the initialization code is running on one thread while other UI code is running on the event-dispatch thread, which breaks Swing's single-threaded access model:
> java BadSwingButton
On the event thread? : true
I'm about to be realized: false
The program in Listing 5 will update the button's label from the container listener when the button is added to the container. To make the scenario more realistic, imagine a UI that "counts" labels in it and uses the count as the text in the border title. Naturally, it would need to update the border's title text in the event-dispatch thread. To keep things simple the program just updates the label of one button. While not realistic in function, this program shows the problem with every Swing program that has been written since the beginning of Swing's time. (Or at least all those that followed the recommended threading model found in the javadocs and online tutorials from Sun Microsystems, and even in my very own early editions of Swing programming books.)
Swing threading done right
The way to get Swing threading right is to forget Sun's original dictum. Don't worry about whether a component is realized or not. Don't bother trying to determine whether it is safe to access something off of the event-dispatch thread. It never is. Instead, create the whole UI on the event-dispatch thread. If you place the entire UI creation call inside an EventQueue.invokeLater()
all accesses during initialization are guaranteed to be done in the event-dispatch thread. It is that simple.
Listing 6. Everything in its place
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GoodSwingButton {
public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
JFrame frame = new JFrame("Title");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Press Here");
ContainerListener container = new ContainerAdapter() {
public void componentAdded(final ContainerEvent e) {
SwingWorker worker = new SwingWorker() {
protected String doInBackground() throws InterruptedException {
return null;
}
protected void done() {
System.out.println("On the event thread? : " +
EventQueue.isDispatchThread());
JButton button = (JButton)e.getChild();
String label = button.getText();
button.setText(label + "0");
}
};
worker.execute();
}
};
frame.getContentPane().addContainerListener(container);
frame.add(button, BorderLayout.CENTER);
frame.setSize(200, 200);
System.out.println("I'm about to be realized: " +
EventQueue.isDispatchThread());
frame.setVisible(true);
}
};
EventQueue.invokeLater(runner);
}
}
Run it now and the above program will show that both the initialization and container code are running on the event-dispatch thread:
> java GoodSwingButton
I'm about to be realized: true
On the event thread? : true
In conclusion
The extra work to create your UI in the event-dispatch thread might seem unnecessary at first. Everyone has been doing it the other way since the beginning of time, after all. Why bother to change now? The thing is, we've always been doing it wrong. To ensure your Swing components are accessed correctly you should always create the entire UI in the event-dispatch thread, as shown here:
Runnable runner = new Runnable() {
public void run() {
// ...create UI here...
}
}
EventQueue.invokeLater(runner);
Moving your initialization code to the event-dispatch thread is the only way to ensure that your Swing GUIs are thread safe. Yes, it will feel awkward at first, but progress usually does.
Learn more about this topic
- Learn more about Swing programming and the event-dispatch thread from one of the masters of Java desktop development: Chet Haase on maximizing Swing and Java 2D (JavaWorld Java Technology Insider podcast, August 2007).
- "Customize SwingWorker to improve Swing GUIs" (Yexin Chen, JavaWorld, June 2003) digs deeper into some of the Swing threading challenges discussed in this article and explains how a customized
SwingWorker
can provide the muscle to work around them. - "Java and event handling" (Todd Sundsted, JavaWorld, August 1996) is a primer on event handling circa AWT.
- "Speed up listener notification" (Robert Hastings, JavaWorld, February 2000) introduces the JavaBeans 1.0 specification for event registration and notification.
- "Achieve strong performance with threads, Part 1" (Jeff Friesen, JavaWorld, May 2002) introduces Java threads. See Part 2 for an answer to the question: Why do we need synchronization?
- "Executing tasks in threads" is a JavaWorld excerpt from Java Concurrency in Practice (Brian Goetz, et al., Addison Wesley Professional, May 2006) that encourages task-based thread programming and introduces an execution framework for task management.
- "Threads and Swing" (Hans Muller and Kathy Walrath, April 1998) is one of the earliest official references for Swing threading. It includes the now famous (and erroneous) "single-thread rule."
- Creating a GUI with JFC/Swing is the comprehensive Java Tutorial page for Swing GUI programming.
- "Concurrency in Swing" is a tutorial on the Swing trail that includes an introduction to the
SwingWorker
class. - JSR 296: Swing Application Framework is currently a specification in progress. Also see "Using the Swing Application Framework" (John O'Conner, Sun Developer Network, July 2007) to learn more about this next step in the evolution of Swing GUI programming.
- The entire Java AWT Reference (John Zukowski, O'Reilly, March 1997) is available free from the O'Reilly Online Catalog.
- John's Definitive Guide to Java Swing, Third Edition (Apress, June 2005) is completely updated for Java Standard Edition version 5.0. Read a preview chapter from the book right here on JavaWorld!
- Visit the JavaWorld Swing/GUI research center for more articles about Swing programming and Java desktop development.
- Also check out the JavaWorld developer forums for discussions and Q&A related to Swing and Java desktop programming.
This story, "Swing threading and the event-dispatch thread" was originally published by JavaWorld.