April 2005
Desktop Java
The following is a sample chapter from SourceBeat’s Destop Java Live by Scott
Delap. You can access more information about this book and our other titles at
www.sourcebeat.com.
SourceBeat’s publishing model is quite different than traditional publishers in that our
books are updated monthly and accessed electronically via secure PDF files. Readers
subscribe annually to each title for $29.95 and receive updates throughout the
subscription life.
Thank you for your interest in this title.
CHAPTER 5
Swing Threading
Desktop applications are often multi-threaded in nature. While adding benefits to responsiveness and
performance, multi-threading adds coding complexity. You can write Swing applications without using
multiple threads. However, once you cross the line into having a multi-threaded application, you
should abide by Swing’s threading rules. Otherwise, your application may behave in an unpredictable
manner. In this chapter, you’ll explore how Swing uses threads. You’ll then view a number of
threading-related resources in the Swing API. You’ll also be introduced to three structured Swing
threading solutions: SwingWorker, Foxtrot, and Spin. Finally, you’ll learn how to detect incorrect
thread usage in Swing with a custom repaint manager.
Swing, AWT, and the EDT
PAGE 142 Desktop Java Live
Swing, AWT, and the EDT
Swing is built on top of Java’s Abstract Windowing Toolkit (AWT). AWT uses native code to
access the underlying operating system’s components. Each component in AWT, such as a
button, has a corresponding native peer that requests use, for example when it needs to draw
that it has been pressed. Swing, on the other hand, does not use native peer components
except for a few rare exceptions. Instead, it paints components completely in Java on top of a
canvas provided by AWT. An example of this is shown in Chapter 4; the AnimatedGradientButtonUI
paints a special effect when a button has focus. Because Swing exists on top of AWT, it
takes advantage of the AWT event-dispatching framework.
When you start a Swing application, three threads are executed: the main application thread, a
toolkit thread and the event dispatch thread. The main application thread calls the main() method
to start the application. The toolkit thread dispatches events from the native operating
system, which make their way to the AWT event dispatch thread, called the EDT. The EDT
handles two primary functions. First, it dispatches events, such as mouse clicks, to the appropriate
Swing component. Second, it executes paint operations of Swing components.
Note: When a thread other than the EDT executes code, it is often described as “off the EDT.”
Correspondingly, code executed by the EDT when it processes events is often described as “on the
EDT.” Finally, using threads other than the EDT in your application causes your application to be
“multi-threaded.”
When a mouse click occurs, for instance on a JButton, an event is generated. AWT dispatches
this event to the Jbutton, which fires a mouse event to its listeners, one of which is its UI delegate.
The delegate then paints the JButton’s visible state to appear pressed. From the time the
event is dispatched until the paint operation is performed, the code is executed on the EDT.
You may be wondering what this has to do with threading, because AWT seems to have the
situation handled. AWT, for the most part, has things under control; however, if you’ve
written Swing code, you’ve probably encountered times when your application froze. Figure
5.1 and the subsequent code show a situation that will cause a freeze.
Swing, AWT, and the EDT
PAGE 143 Chapter 5: Swing Threading
Figure 5.1: A JButton Experiencing a Freeze after Mouse Down
public JPanel createPanel() {
JPanel panel = new JPanel();
panel.setLayout(new FormLayout("p:g", "p, p, f:d:g"));
CellConstraints cc = new CellConstraints();
JButton button = new JButton(new FreezeAction());
panel.add(button, cc.xy(1, 1));
JButton button1 = new JButton("Other Button");
panel.add(button1, cc.xy(1, 2));
Vector values = new Vector();
for (int i = 0; i < 100; i++) {
values.add("Value" + i);
}
JList list = new JList(values);
panel.add(new JScrollPane(list), cc.xy(1, 3));
return panel;
}
private class FreezeAction extends AbstractAction {
Swing, AWT, and the EDT
PAGE 144 Desktop Java Live
At first glance, you see two buttons and a list contained in a panel. However, notice the
Thread.sleep() method calls in the appropriately named FreezeAction. Clicking the Freeze EDT
button will result in the button staying “stuck” in a pressed state for five seconds. The button
doesn’t return to its normal state because the sleep() method is applied to the EDT, and stops
any further processing of input or paint events.
public FreezeAction() {
super("Freeze EDT");
}
public void actionPerformed(ActionEvent e) {
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
}
}
}
The Event Queue and Processing
PAGE 145 Chapter 5: Swing Threading
The Event Queue and Processing
Events, such as moving the mouse, are created and placed in a queue called the EventQueue.
The EDT then processes these events sequentially. Figure 5.2 and the subsequent code
expand on the previous example, illustrating some of the input events processed by the EDT.
Figure 5.2: Echoing Events as They Are Processed by the EDT
In this example, an AWTEventListener is added to the default toolkit.
public JPanel createPanel() {
JPanel panel = new JPanel();
panel.setLayout(new FormLayout("p:g", "p, p, p, f:d:g"));
CellConstraints cc = new CellConstraints();
...
JTextArea textArea = new JTextArea();
textArea.setAutoscrolls(false);
JScrollPane textScrollPane = new JScrollPane(textArea);
textScrollPane.setHorizontalScrollBarPolicy(
The Event Queue and Processing
PAGE 146 Desktop Java Live
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
textScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
panel.add(textScrollPane, cc.xy(1, 4));
Toolkit.getDefaultToolkit().addAWTEventListener(
new EchoingAWTListener(textArea),
AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
return panel;
}
private class ToggleEchoAction extends AbstractAction {
public ToggleEchoAction() {
super("Echo AWT Events");
}
public void actionPerformed(ActionEvent e) {
EDTListenerExample.this.echo = !EDTListenerExample.this.echo;
if (EDTListenerExample.this.echo) {
putValue(Action.NAME, "Disable Echo");
} else {
putValue(Action.NAME, "Echo AWT Events");
}
}
}
private class EchoingAWTListener implements AWTEventListener {
private JTextArea textArea;
private int count;
public EchoingAWTListener(JTextArea textArea) {
this.textArea = textArea;
The Event Queue and Processing
PAGE 147 Chapter 5: Swing Threading
}
public void eventDispatched(AWTEvent event) {
if (EDTListenerExample.this.echo) {
String eventName = getEventType(event.getID());
if (eventName != null) {
this.count++;
textArea.append("/n" +
this.count + " - " + eventName);
}
}
}
private String getEventType(int id) {
switch (id) {
case KeyEvent.KEY_PRESSED:
return "KEY_PRESSED";
case KeyEvent.KEY_RELEASED:
return "KEY_RELEASED";
case MouseEvent.MOUSE_PRESSED:
return "MOUSE_PRESSED";
case MouseEvent.MOUSE_RELEASED:
return "MOUSE_RELEASED";
case MouseEvent.MOUSE_MOVED:
return "MOUSE_MOVED";
case MouseEvent.MOUSE_DRAGGED:
return "MOUSE_DRAGGED";
case MouseEvent.MOUSE_ENTERED:
return "MOUSE_ENTERED";
case MouseEvent.MOUSE_EXITED:
return "MOUSE_EXITED";
case MouseEvent.MOUSE_WHEEL:
return "MOUSE_WHEEL";
default:
The Event Queue and Processing
PAGE 148 Desktop Java Live
In this example, an AWTEventListener is added to the default toolkit.
Note: An AWTEventListener can slow your application down because it is executed on every AWT
event.
This listener is triggered every time an event is processed. Clicking the Echo AWT Events
button lists (or echoes) all mouse and keyboard events in the text area. For instance, if you run
this example and click the Freeze EDT button, ‘MOUSE_PRESSED’ will appear in the text
area, which corresponds to clicking the mouse. At this point, the Thread.sleep() method begins
to run, so the EDT can no longer process events. As a result, events stay queued; no events
are processed until the Thread.sleep() method is finished. Upon the method’s completion, the
queued events are processed sequentially (including the MOUSE_RELEASED event after
clicking the button).
Freeze issues in most Swing applications occur when long running (or heavyweight) operations
on the EDT are executed, which blocks the processing of other events. Often, it is not
obvious that such operations are running on the EDT. By default, all Swing operations
including, actionPerformed() methods in actions and listeners are executed on the EDT. You will
see a number of ways to free up the EDT later in this chapter. First, you need to be aware of
the Single Threading Rule of Swing.
The Single Threading Rule
The Swing Single Threading Rule states the following:
To avoid the possibility of deadlock, you must take extreme care that Swing components
and models are created, modified, and queried only from the event-dispatching
thread.1
return null;
}
}
}
1. Sun Microsystems, Inc. “How to Use Threads.” Sun Microsystems, Inc. 1995-2005. http://java.sun.com/
docs/books/tutorial/uiswing/misc/threads.html
The Event Queue and Processing
PAGE 149 Chapter 5: Swing Threading
Swing was designed with the assumption that all access to Swing components is executed on
the EDT. Consequently, Swing components are not designed to be thread-safe. Therefore,
any time you access Swing components from threads other than the EDT, common threading
problems may exist, such as race conditions and deadlocks. The Single Threading Rule has
evolved slightly over the years. It was once thought that components could be accessed from
the EDT as long as you didn’t modify them after they had been realized, that is, painted or
ready to be painted. Using this definition, the following code was considered safe:
However, Sun’s engineers found that in a few of the Swing tutorial examples, even unrealized
components being accessed from the EDT caused problems. For instance, one of the official
Swing tutorial examples intermittently had deadlock problems. Therefore, component
creation should only occur on the EDT just like other Swing component manipulation. You
will learn how to put code, such as the frame creation example, on the EDT from another
thread later in this chapter.
Why Single Threading?
The single-threaded architecture of Swing is useful for a number of reasons.