This weekend I created an easy way to find if you are modifying Swing components on nonswing threads. At the Desktop Threading Session at JavaOne they suggested using java.lang.reflect.Proxy to proxy calls and do a check for the Event Dispatch Thread (EDT). So you would have:

class ThreadChecker implements InvocationHandler {
    public Object invoke(Object proxy, 
                         Method method, Object[] args) throws Throwable {
        if (!SwingUtilities.isEventDispatchThread() {
            Thread.dumpStack();
        }
    }
}

This works but who wants to modify all of their code to proxy things. Instead of taking your thread checking code to each class, why not have your classes come to your thread checking code? So I began to ask myself what common piece of code do all Swing classes use that I could easily intercept ... RepaintManager! It can be replaced by calling:

RepaintManager.setCurrentManager(new MyRepaintManager());

With this knowledge I created the following class just over 20 lines later:

public class ThreadCheckingRepaintManager extends RepaintManager {
    public synchronized void addInvalidComponent(JComponent jComponent) {
        checkThread();
        super.addInvalidComponent(jComponent);
    }

    private void checkThread() {
        if (!SwingUtilities.isEventDispatchThread()) {
            System.out.println("Wrong Thread");
            Thread.dumpStack();
        }
    }

    public synchronized void addDirtyRegion(JComponent jComponent, int i,
                                            int i1, int i2, int i3) {
        checkThread();
        super.addDirtyRegion(jComponent, i, i1, i2, i3);
    }
}

Next I wanted to test my invention. I plugged my new repaint manager in to the application I've been developing for 3+ years and got a big fat nothing. Turns out our application plays pretty nicely with the EDT. So I created these three actionPerformed methods:

BadSwingExample:
This code performs work simulated with Thread.sleep(1000). However since the action is invoked on the EDT the Thread.sleep causes the EDT to sleep. This will lead to repaint issues. Here is a screenshot of this code being executed as the button is clicked. Notice the button is still blue. It is not allowed to repaint until after the Thread.sleep()

        public void actionPerformed(ActionEvent event) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 100; i++) {
                val++;
                listModel.addElement(new Integer(val));
            }
        }

BadThreadSwingExample:
This code offloads the work but then modifies the listModel on the nonswing thread. This can lead to unpredictable results.

        public void actionPerformed(ActionEvent event) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    for (int i = 0; i < 100; i++) {
                        val++;
                        listModel.addElement(new Integer(val));
                    }
                }
            }).start();
        }
    }

GoodSwingSwingExample:
This example offloads the work from the EDT but uses invokeLater() to place the code that modifies the listModel back on the EDT.

        public void actionPerformed(ActionEvent event) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            for (int i = 0; i < 100; i++) {
                                val++;
                                listModel.addElement(new Integer(val));
                            }
                        }
                    });
                }
            }).start();
        }

After this I fired things up in IntelliJ and watched as the stacktraces started showing up when I ran the BadThreadSwing example:

Here is the source. I've included a dialog that easily allows you to turn the ThreadCheckingRepaintManager on/off while running the examples.

Followup Entry:
There is a followup entry that handles threading of nonrealized components.

Followup Entry:
This blog entry has a second followup here:
Easily Find Swing Threading Issues - Part III


Nice. This is a pretty common problem and often results in a deadlock. Configuring this behaviour with a switch and running it in dev and QA with this on should be a good way to catch these mistakes. Thanks for the post.
That's an awesomely simple and powerful solution. It's just the kind of visibility into a running program that makes solving a knotty problem an enjoyable programming exercise.
Very nice. I do all sorts of nasty event stuff and it'll be interesting to see if it works as well as I think it does ;-)
This is good stuff... perhaps there'd be a good way to integrate this into a unit test testcase so that tests fail if we start playing around with components outside the swing thread.
This approach seems to flag the creation of a JComponent on a non-EventDispatch Thread as incorrect. As per most all the documentation and per a conversation I had with Amy Fowler at JavaOne, this is acceptable. You are not allowed to update a GUI component once it has been rendered visible. Please correct me if I am wrong. When I use the given technique above on my Application, I get a OutOfMemoryException. I guess it is so busy printing stack traces it dies :-)
That is an issue. I'm debating on how to address it. Probably I'll end up parsing the stacktrace. I would recommend changing out the RepaintManager after your components have been created. However, I realize that it may not be easy to do this given the complexity of a given application and how it initializes components.
That is correct. The thread check should only apply to components that are visible. I have modified my source to check the component's root container to see if it is null or invisible. If so, I just skip the thread check.
Great idea and thank you for sharing it with us! I'll definitely use it. In case you haven't noticed - to close example application <i>RunSwingExamples</i> you have to define method <i>windowClosing(WindowEvent e)</i>.
<i>This approach seems to flag the creation of a JComponent on a non-EventDispatch Thread as incorrect.</i> <p/> Sun changed their recommendations and now recommends the UI be built on the EDT, so I think flagging creation on non-EDT is OK. <p> "We used to say that you could create the GUI on the main thread as long as you didn't modify components that had already been realized. While this worked for most applications, in certain situations it could cause problems." <br> http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
One could argue that this is simply a defect in Suns code. Something they could/should fix. Seems "odd" that the swing team would change their minds after how many years :-)
One could argue that this is simply a defect in Suns code. Something they could/should fix. Seems "odd" that the swing team would change their minds after how many years :-)
This is nice but unfortunately does not handle exceptions to the rule published by Sun at<br><br> http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html#exceptions
That is true but it shouldn't be hard to parse the stacktrace for calls from those methods and exclude them. Hopefully the ThreadCheckingRepaintManager can be refined as time goes along to handle such situations. I already have an addition that dumps the component tree of a component that I will post soon. Even if the class isn't perfect, it should at least provide a starting point from a debug perspective.
> One could argue that this is simply a defect in Suns code. Something they could/should fix. <br> > Seems "odd" that the swing team would change their minds after how many years :-) <p> Swing's rules on threading have been problematic from the start. While creating components on another thread it is trivial to start generating events that are delivered to the component on the event dispatch thread resulting in two threads hitting the same component. Something Swing was not designed to deal with. As we've started to see more and more problems happen as a result of this we decided to revise our rules to be more realistic. <p> It's true that often times you can get away with creating components on another thread and never see any problems, but it's much easier for us to say create everything on the event dispatching thread and don't worry about it. Ideally we would have an Application class that provides a place for you to create your GUI and isolated you from the main thread/event dispatching thread differences. We're actively investigating this now.isolates the differences in main thread and event dispatch thread from you, and
Hi The ThreadCheckingRepaintManager class is extremely useful, literally saved me hours of debugging EDT issues with swing components for our project I wish something like this was part of the JBuilder IDE (or is it?) thx, anil


Please note all comments are moderated due to spam concerns. Your comment may not immediately appear once submitted.

Add a comment

Title
Body
Name
E-mail address
Website
Remember me Yes  No 
  • E-mail addresses are not publicly displayed - please leave your e-mail address if you would like to be notified when new comments are added to this blog entry (you can opt-out later).
  • Allowed HTML/XHTML tags : b, i, blockquote, br, p, pre, a href="", ul, ol, li
TrackBack : http://www.clientjava.com/blog/addTrackBack.action?entry=1093059428000