Skip to main content

The Single Thread Rule in Swing

Posted by cayhorstmann on June 11, 2007 at 9:44 PM PDT

The Single Thread Rule in Swing

??? style="float: left; margin-right: 1em" />I am working on the 8th edition
of Core Java, and I just
received a batch of very thoughtful comments from href="http://www.briangoetz.com/">Brian Goetz about the concurrency
chapter. (Thanks Brian!!!) He points out that I missed an important change
in the Swing single thread rule. (Judging by href="http://bitguru.wordpress.com/2007/03/21/will-the-real-swing-single-threading-rule-please-stand-up/">this
blog, I am not the only author who did.)

The original rule is well explained in href="http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html">this
classic article. It is:

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.

“Realized” is defined in that article, and it is a bit
intricate. But for the most part, it means that you should stop messing
with Swing components in the main thread after calling pack (!)
or setVisible(true), whichever comes first.

???For example, the following code would be
bad.

public class BadExample
{
   public static void main(String... args)
   {
      JFrame frame = new JFrame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
      frame.setSize(300, 300); // BAD
   }
}

But the href="http://java.sun.com/javase/6/docs/api/javax/swing/package-summary.html#threading">latest
Javadoc goes quite a bit further. It says “All Swing components
and related classes, unless otherwise documented, must be accessed on the
event dispatching thread.”

??? style="float: left; margin-right: 1em" />In other words, this example is
also bad:

public class StillBad
{
   public static void main(String... args)
   {
      JFrame frame = new JFrame(); // BAD
      frame.setSize(300, 300); // BAD
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // BAD
      frame.setVisible(true); // BAD
   }
}

The Javadoc then goes on: “The preferred way to transfer control
and begin working with Swing is to use invokeLater.”

??? style="float: left; margin-right: 1em" />Here is the example, rewritten
the good way.

public class PureGoodness
{
   public static void main(String... args)
   {
      EventQueue.invokeLater(new Runnable()
         {
            public void run()
            {
               JFrame frame = new JFrame();
               frame.setSize(300, 300);
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setVisible(true);
            }
         });
   }
}

Ugh. That won't win the hearts and minds of my students. I suppose I
could rewrite it as

public class PureGoodness implements Runnable
{
   public void run()
   {
      JFrame frame = new JFrame();
      frame.setSize(300, 300);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
   }

   public static void main(String... args)
   {
      EventQueue.invokeLater(new PureGoodness());
   }
}

Still ugh.

I am left with some questions and I fervently hope that some of you
have answers:

  1. What does it mean that this is the “preferred” way? What
    other non-bad ways are there? Or is the bad approach not so bad after
    all?
  2. How can I come up with a tangible example of the badness of the bad
    approach? href="http://bitguru.wordpress.com/2007/03/21/will-the-real-swing-single-threading-rule-please-stand-up/">Rumor
    has it that href="http://java.sun.com/docs/books/tutorial/uiswing/examples/events/ComponentEventDemoProject/src/events/ComponentEventDemo.java">ComponentEventDemo
    would deadlock on Solaris if the GUI was created in the main thread, but
    that program has other threading problems, as evidenced by this
    comment:
    protected void displayMessage(String message) {
    //If the text area is not yet realized, and
    //we tell it to draw text, it could cause
    //a text/AWT tree deadlock. Our solution is
    //to ensure that the text area is realized
    //before attempting to draw text.
        if (display.isShowing()) {
            display.append(message + newline);
            display.setCaretPosition(display.getDocument().getLength());
        }
    I tried creating a bad situation, but I was stymied by the fact
    that on Linux (and, I believe, on Windows) the event dispatch thread is
    never created until the call to setVisible. Core Java has a
    convincing example that shows a combo box breaking when another thread
    adds and removes items after the event dispatch thread has started. I
    would very much like something similar that convinces readers to use
    invokeLater when constructing the UI.
  3. The Javadoc says that you should call href="http://java.sun.com/javase/6/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)">SwingUtilities.invokeLater.
    According to that method's Javadoc, “As of 1.3 this method is just
    a cover for java.awt.EventQueue.invokeLater().” So, why
    not recommend to use EventQueue.invokeLater? It seems clearer,
    and it is four letters shorter.
  4. Should I really go through the trouble of style="text-decoration: line-through;">uglifying correcting
    dozens of Swing examples in Core Java? Is it worth worrying about it?
    After all, there must be thousands of programs out there that rely on
    the old version of the single thread rule.