by John Zukowski

Swing threading and the event-dispatch thread

how-to
Aug 21, 200718 mins

The evolution and pitfalls of Swing's single-threaded event model

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.)

Enter the JavaBeans component model

The Java 1.1 platform release (note that the Java Enterprise Edition didn’t yet exist) introduced something called the JavaBeans paradigm. It wasn’t limited to GUI development but was most prevalently used there. JavaBeans event handling didn’t require you to subclass your component to add behavior; instead, you literally added implementations of interfaces to the component with methods like addActionListener(), addMouseListener(), and others.

The Listener was (and still is) a sub-interface of the java.util.EventListener interface. When a triggering event happened, the component would loop through the list of registered listeners and notify them. The listener would then respond by executing its custom business logic, without requiring you to subclass.

Java developers still are using the JavaBeans model for event handling today. To demonstrate, here’s a simple program that prints a message when a button is pressed:

Listing 2. AWT button

import java.awt.*;
import java.awt.event.*;

public class HelloButton {
  public static void main(String args[]) {
    Frame frame = new Frame("Title");
    Button button = new Button("Press Here");
    ActionListener action = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.out.println("Clicked");
      }
    };
    button.addActionListener(action);
    WindowListener window = new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    };
    frame.addWindowListener(window);
    frame.add(button, BorderLayout.CENTER);
    frame.setSize(200, 200);
    frame.setVisible(true);
  }
}


I should point out a couple things here. First, the button component in Listing 2 was written using the AWT (Abstract Window Toolkit) component set, because the Swing component set didn’t yet exist when the JDK 1.1 first came out. Second, as you can see in the above code, the program is based on the event pattern originally described in the official Sun documentation for GUI application development. With the AWT component set, nobody cared if your code ran on the event thread or not. Instead, the GUI was created on the program’s main thread.

Just for fun, Figure 1 shows the button that would result from this code.

Figure 1. A button component created using AWT

Swing replaces AWT

The native, peer-based component set known as AWT had difficulties with platform irregularities, causing the same application to behave differently on different desktop platforms. Instead of continuing to rely on AWT’s native widget sets for each platform, the Swing component set was born, creating a purely Java based, peerless component set. This, of course, also meant learning a whole new set of tricks for GUI development. (While you can still use the AWT component set today, most developers have moved over to the Swing component set.)

What is a peer?
In AWT a peer is basically the native component specific to a given desktop platform. The Swing component set is peerless, so rather than calling a native widget, as you would with AWT, Swing gives you a blank canvas. You then paint and control the component completely from within the Java platform.

Before getting into the specifics of Swing threading, I should mention something about the timeframe of the Swing toolkit release. When Swing first became available, Java 1.1 was the primary release available. Java 1.2 was emerging but wasn’t fully available yet. To manage this, Sun packaged up Swing to run with the JDK 1.1 with the help of a supplemental JAR file, as well as making it a standard part of JDK 1.2.

Dual packaging meant that the Swing component set couldn’t rely on any APIs specific to Java 1.2. Sun handled this by adding some methods to a class called SwingUtilities to wrap some of the 1.2 API calls in JDK 1.1 applications. The SwingUtilities class would include the full implementations of its helper methods in Java 1.1 application, whereas Java 1.2 applications could just have SwingUtilities redirect the call to the wrapped real method.

Something funny happened along the way, though: people never stopped calling the wrapped methods. Not having to support the older code made it possible to call methods like invokeLater(), invokeAndWait(), and isEventDispatchThread() (which I’ll discuss momentarily) directly on the java.awt.EventQueue object, instead of indirectly through the SwingUtilities class.

Swing threading

As Java GUI developers advanced from AWT to Swing, one of the key things pushed by Sun was the Swing toolkit’s single-threaded programming model. Here’s the single-thread rule from an early introduction to Swing threading:

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.

— Hans Muller and Kathy Walrath,

Threads and Swing

Swing’s single-threaded approach removed the need to make GUI components thread safe by basically saying “do everything on the event-dispatch thread once the component is realized.” In this context, a realized component is one whose paint() method could have been called, such as a component shown within a visible window. Once a component has been realized the system always executes event-handling code and draws screens and their components on the event-dispatch thread.

Updating the AWT button example to this early Swing initialization model results in the program shown in Listing 3. The new program adds one behavior: after pressing the button it waits three seconds before updating the button label. I’ve also taken advantage of the Swing JFrame‘s setDefaultCloseOperation() method, which wasn’t available when using the AWT Frame class.

Listing 3. A typical Swing button

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class SwingButton {
  public static void main(String args[]) {
    JFrame frame = new JFrame("Title");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    final JButton button = new JButton("Press Here");
    ActionListener action = new ActionListener() {
      Runnable changer = new Runnable() {
        public void run() {
          String label = button.getText();
          button.setText(label + "0");
        }
      };
      Runnable sleeper = new Runnable() {
        public void run() {
          try {
            Thread.sleep(3000);
            EventQueue.invokeLater(changer);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      };
      public void actionPerformed(ActionEvent e) {
        System.out.println("Clicked");
        new Thread(sleeper).start();
      }
    };
    button.addActionListener(action);
    frame.add(button, BorderLayout.CENTER);
    frame.setSize(200, 200);
    frame.setVisible(true);
  }
}

Figure 2 shows the button that would result from the code in Listing 3.

A button component created using the Swing toolkit

Figure 2. A button component created using the Swing toolkit

Notes about the code

SwingButton adds some additional behavior to the program shown in Listing 2 — it updates the button’s text label by taking the old button label and appending a 0 to it. You can see the code to do this in the actionPerformed() behavior of the button click:

System.out.println("Clicked");
new Thread(sleeper).start();

This code says print “Clicked” to the console, start a new thread, and do the code from the sleeper runnable. Ignoring the exception handling, that code looks like so:

Thread.sleep(3000);
EventQueue.invokeLater(changer);

The first question you might ask is why not just sleep in the actionPerformed() method. The answer is simple: if the sleep() call happens there, the GUI will become non-responsive. In both Swing and AWT, the GUI handling code executes in the event-dispatch thread. This includes painting the UI.

You can test out this non-responsiveness by changing the above program’s start() call to a run() call and then running the program. This essentially prevents a new thread from being created. You’ll notice the button continues to look pressed-in for three seconds before it becomes unselected with the new button label. Another option would be to move the Thread.sleep() call into its own thread. You could then press the button multiple times in quick succession and it would look like the button had been pressed multiple times. After waiting three seconds after each press, the label would be updated.

Avoid a common error
Don’t call the invokeAndWait() method of EventQueue from the event-dispatch thread. If you do, the event-dispatch thread will be blocked until the requested task runs on it. This latter task will never happen, so the event-dispatch thread will be forever blocked.

All of this brings me to the invokeLater() call. As I previously mentioned, updates to the Swing component state must be done on the event-dispatch thread. The EventQueue class has two methods to help with running code on the event thread: invokeLater() and invokeAndWait(). The invokeLater() method says to take the Runnable argument and have the thread scheduler run it at some later time on the event thread. The invokeAndWait() method says the same thing but with one added caveat: don’t continue running the current thread until the invoked Runnable method finishes.

Looking back at the complete button-click behavior, it says to start a new thread and sleep for three seconds. On the event-dispatch thread it then gets the button label and appends a 0 to it. Forcing the get and set label methods to happen on the event-dispatch thread is an easy way to avoid thread synchronization, because the Swing component’s state is only ever accessed on one thread.

Introducing SwingWorker

Despite all this talk of invokeLater() and updating the button label, the sample program in Listing 3 still doesn’t start up properly. What you’ve seen so far is how the first generation of Swing developers learned to start up our Swing GUIs. Soon you’ll learn what has changed since then and how to properly start up your Swing applications. But first I want to introduce the SwingWorker class.

SwingWorker is new to Java SE 6 but has been around in one form or another since the early days of the Java platform (via Sun’s online Java tutorial). You’ll notice in the above Swing program that the actionPerformed() method created a helper thread to do a task, and then had to call back to do something on the event-dispatch thread once that task was complete. This is a common behavior because longer tasks should not be executed on the event-dispatch thread. Ultimately, though, you should update Swing component(s) in your user interface on the event-dispatch thread. SwingWorker helps you update components without having to deal with the logistics of thread creation (i.e., knowing what thread to create when).

See this article’s Resources section for a more complete tutorial on using SwingWorker. In its simplest form, here’s how to use this class:

  • Override the doInBackground() method to run any code on a secondary thread. Use the get() method to retrieve anything calculated during the run of doInBackground().
  • Override the process() and/or done() methods to run any code on the event-dispatch thread.
  • Call execute() to cause the worker to be scheduled to run.

Here’s the Swing button program re-written to take advantage of SwingWorker:

Listing 4. A little help from SwingWorker

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class SwingButton2 {
  public static void main(String args[]) {
    JFrame frame = new JFrame("Title");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    final JButton button = new JButton("Press Here");
    ActionListener action = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.out.println("Clicked");
        SwingWorker worker = new SwingWorker() {
          protected String doInBackground() throws InterruptedException {
            Thread.sleep(3000);
            return null;
          }
          protected void done() {
            String label = button.getText();
            button.setText(label + "0");
          }
        };
        worker.execute();
      }
    };
    button.addActionListener(action);
    frame.add(button, BorderLayout.CENTER);
    frame.setSize(200, 200);
    frame.setVisible(true);
  }
}

Note that your program must create SwingWorker each time it will execute. Once the task completes the worker is literally done. Also notice that the overridden methods are protected. SwingWorker is an abstract class with behavior behind it, not just an interface. The doInBackground() method must return something, but otherwise all it does is sleep. All done() does is update the label. SwingButton2 is a much cleaner version of the Swing button program. That said, note that you don’t have to use SwingWorker to optimize the Swing button from Listing 3. If you did not use SwingWorker you would just need to be sure to run each aspect of the event handling in the correct thread.

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.

John Zukowski has been playing with Java for well over 12 years now, having abandoned his C and X-Windows mindset long ago. With 10 books out on topics from Swing to collections to Java SE 6, John now does strategic technology consulting through his business, JZ Ventures, Inc..