Do you ever get the feeling there's something not quite right about Swing threading? John Zukowski puts his finger on it in this article. While tracing Swing's single-threaded event model from Java 1.0 to its origins in the JavaBeans component model and AWT, he reveals an initialization bug in a commonly used start-up pattern. Fortunately, once you know about the bug, you can fix it. All you have to do is update your interpretation of Swing's single-thread rule.
Graphical user interface programming has evolved significantly in the relatively short lifespan of the Java platform. From the now extinct Java 1.0 event model through the JavaBeans component model to the introduction of the single-threaded Swing component set, Java developers have continually had to adapt to the latest trends in Swing GUI development. With the new Swing Application Framework (JSR 296) on the horizon, even more changes are soon to come.
In this article, you'll learn about the evolution of Swing GUI programming, with particular attention to Swing threading. I'll walk you down the memory lane of event handling in Java desktop applications and explain how earlier event models relate to the single-threaded programming model used today. I'll also demonstrate a bug in a commonly used start-up pattern and explain how it breaks the thread safety of Swing applications. Finally, I'll show you how to work around it, either with the help of the SwingWorker
class or without it.
The Java 1.0 event model
GUI programming for what was then known simply as Java (rather than the Java platform) started life with a wacky containment model for event handling. Instead of simply adding or removing event listeners as we do today, you had to override one of a series of methods and then have the component return true to indicate that it had handled the event. For instance, if you wanted to execute a mouse-down event, you had to subclass the component and include something like the following method:
Listing 1. Event handling in Java 1.0
public boolean mouseDown(Event e, int x, int y) {
System.out.println("Coordinates: " + x + " - " + y);
return true;
}
Returning true told the component's container that the event had been handled. If the method returned false (its default behavior), the container would know that the event was unprocessed and would be offered the event for processing. This would happen until some container processed the event and returned true, or until you came to the top-level window, in which case the unprocessed event would be ignored.
To state this in a slightly different way: If a button was in a container, which was in a container, which was in a frame, there would be four checks for each event: one at the button level itself and three in its containers. To reduce the number of containers, you could do everything with GridBagLayout
-- but why make matters even more complicated?
Further challenges of the Java 1.0 event model
The Java 1.0 event model placed two very odd requirements on a Java-based system. First off, the entire containment hierarchy was queried for every event generated, to see if any of them wanted to process the event. Typically, they all returned false, except for a handful of cases where the event was to be handled. All the unnecessary calls through the hierarchy created a lot of overhead and slowed down the system.
The second issue was that you had to constantly subclass components to change their behavior. You couldn't simply attach a listener to a component that said "when event x happens, do y." Having an event handled by multiple containers/listeners introduced even more complexity to the situation.
There were other issues with the Java 1.0 event model. For instance, if you forgot to call an overridden handleEvent()
, no events progressed up the containment hierarchy. Relying on the Container
to handle events for its multiple components made each of the event-handling methods even more complicated. The Container
had to check the source for each event before processing.
Let's just say that the Java 1.0 event model died a quick death when the JavaBeans component model was introduced with the JDK 1.1 release. Thankfully, you couldn't mix the two models in one program. (See this article's Resources section to learn more about the Java 1.0 event model.)