Swing threading and the event-dispatch thread

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

1 2 3 4 5 Page 4
Page 4 of 5

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.

1 2 3 4 5 Page 4
Page 4 of 5