The Single Thread Rule in Swing

???I am working on the 8th edition of Core Java, and I just received a batch of very thoughtful comments from 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 this blog, I am not the only author who did.)

The original rule is well explained in 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 callingpack (!) 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 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.”

???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 useinvokeLater.”

???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? Rumor has it that 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 SwingUtilities.invokeLater. According to that method's Javadoc, “As of 1.3 this method is just a cover forjava.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 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.