Thread Checking Swing

I’m getting more and more the feeling that annotating the Java API side, primarily Swing, will be more important and hopefully produce more results than just annotating the DrJava side. I’ve collected a small number of methods alreaydy by grepping the Java API source, but picking the methods this way is extremely cumbersome, both because it requires reading a lot of code and because writing the method signatures by hand actually isn’t all that easy:


















I think I may after all write a GUI tool, a class file browser, that scans the classpath and then allows you to click your way through the packages, classes and methods and attach annotations. I had that idea before, but I just didn’t get to it and then thought maybe it wasn’t necessary.

I’ve also browsed the web and tried to find references for what is and what isn’t allowed with Swing. It’s sad that there isn’t too much that really seems authorative, and the comments in the API source are more than sparse either. Here’s what I’ve found, in no particular order, sometimes with quotes, emphasis by me:

  • Threads and Swing

    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.

    The single-thread rule: Realized means that the component’s paint() method has been or might be called. A Swing component that’s a top-level window is realized by having one of these methods invoked on it: setVisible(true), show(), or (this might surprise you) pack(). Once a window is realized, all components that it contains are realized. Another way to realize a component is to add it to a container that’s already realized.

    Comment: I guess that means I could add a flag or poll some methods when I’m doing the @OnlyRunBy(@ThreadDesc(eventThread=true)) check. But as explained farther below, Sun now recommends doing all GUI construction in the event thread using invokeAndWait.

    Exceptions to the rule: A few methods are thread-safe: In the Swing API documentation, thread-safe methods are marked with this text: This method is thread safe, although most Swing methods are not.

    Comment: I’ll have to grep for that. I tried, but it’s not spelled the same way in all instances. Anyway, these methods will probably be pretty rare.

    An application’s GUI can often be constructed and shown in the main thread.

    Comment: Again, Sun now recommends doing all GUI construction in the event thread using invokeAndWait, so I guess this is out-dated.

    The following JComponent methods are safe to call from any thread: repaint(), revalidate(), and invalidate(). The repaint() and revalidate() methods queue requests for the event-dispatching thread to call paint() and validate(), respectively. The invalidate() method just marks a component and all of its direct ancestors as requiring validation.

    Listener lists can be modified from any thread: It’s always safe to call the addListenerTypeListener() and removeListenerTypeListener() methods. The add/remove operations have no effect on an event dispatch that’s under way.

    Comment: Nice. I have to test it, but I should be able to annotate these methods with @OnlyRunBy(@ThreadDesc(name=".*")) and therefore allow all threads to call them. It should override more restrictive annotations. Other articles mention a few more methods.

  • The Last Word in Swing Threads

    Swing’s single-thread rule says that Swing components can only be accessed by a single thread. This rule applies to both gets and sets, and the single thread is known as the event-dispatch thread.

    Comment: Gets for some primitive data might actually be safe, but for references it’s definitely unsafe.

    Swing components will generally not comply with the single-thread rule unless all their events are sent and received on the event-dispatch thread. For example, property-change events should be sent on the event-dispatch thread, and model-change events should be received on the event-dispatch thread.

    For model-based components such as JTable and JTree, the single-thread rule implies that the model itself can only be accessed by the event-dispatch thread.

    Comment: Somehow I thought that model code could be accessed outside the event thread. It’s good to know that this is not the case. I wonder where that will draw the boundary in DrJava.

  • Painting in AWT and Swing

    It is not recommended that programs invoke paint() directly.

    Programs should not invoke this method [paintImmediately()] directly unless there is a valid need for real-time painting.

    Comment: That implies they should only be run by the event thread.

    The program invokes repaint() on the component, which registers an asynchronous request to the AWT that this component needs to be repainted. The AWT causes the event dispatching thread to invoke update() on the component. NOTE: If multiple calls to repaint() occur on a component before the initial repaint request is processed, the multiple requests may be collapsed into a single call to update().

    Comment: That explains why repaint(), etc. is safe.

  • Concurrency In Swing Text

    The primary form of concurrency support in the text package is designed to support building and maintenance (also called mutations) of the document model on a separate thread from the event dispatching thread. A concurrent application will typically interact with the model and have no direct interest in the GUI.

    Comment: So the models of text components can be accessed from outside the event thread, but those of JTable and JTree can’t? I’ll have to check that more closely, but I guess this was what I remembered.

    The most time-consuming part of laying out a large box is calculating preferred sizes for the children, and then waiting for the actual layout that the children perform. If these two operations are performed synchronously, it may even turn out that a lot of this work is useless.

    Comment: It seems like text components use additional “layout threads”. May they access methods that can only be run by the event thread?

  • Swing : Java Glossary

    There are a few exceptions to the general rule of having to invoke Swing methods from the EventDispatchThread, most notably it is safe to call to the following methods from any thread:
    Component.repaint
    JComponent.revalidate
    JEditorPane.replaceSelection
    JTextArea.append
    JTextArea.insert
    JTextArea.replaceRange
    JTextComponent.replaceSelection
    JTextComponent.setText
    JTextPane.insertIcon
    JTextPane.insertLogicalStyle
    JTextPane.setCharacterAttributes
    JTextPane.setParagraphAttributes
    PlainDocument.setText

    Comment: I need to check that these are actually correct. Also, Component.invalidate was missing from this list.

  • How to Use Threads

    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. […] To avoid the possibility of thread problems, we recommend that you use invokeLater to create the GUI on the event-dispatching thread for all new applications.

    Comment: That means I will not write a special case for access to event thread-only methods if the components haven’t been realized yet.

  • Multithreaded Swing Applications

    If you modify Swing component data from any thread other than the event dispatching thread, you must take precautions to ensure data integrity. An exception to this rule is the setText method on a JTextComponent or any of its subclasses, or any Swing component method whose documentation explicitly states it is thread safe.

    Comment: Interesting… Have to check that and may mark them as @OnlyRunBy(@ThreadDesc(name=".*")).

As a summary, I can probably say that most of Swing should be event thread-only, except for the few methods mentioned in the list below. Text models may be ok to be called from outside the event thread, but JTable and JTree models are not. And Sun has made my life a little bit easier by retracting their statement that a GUI can be constructed in the main thread as long as it hasn’t been realized.

Component.repaint
Container.invalidate
JComponent.revalidate
JEditorPane.replaceSelection
JTextArea.append
JTextArea.insert
JTextArea.replaceRange
JTextComponent.replaceSelection
JTextComponent.setText
JTextPane.insertComponent
JTextPane.insertIcon
JTextPane.setLogicalStyle
JTextPane.setCharacterAttributes
JTextPane.setParagraphAttributes
DefaultStyledDocument.setLogicalStyle
PlainDocument.setText

The comments for DefaultMutableTreeNode indeed say it’s not thread-safe. However, this does not really mean that it should only be accessed by the event thread; there just has to be additional synchronization in place. Can I check for that — without using full-scale Eraser? Should I force access from within the event thread?

* This is not a thread safe class.If you intend to use
* a DefaultMutableTreeNode (or a tree of TreeNodes) in more than one thread, you
* need to do your own synchronizing. A good convention to adopt is
* synchronizing on the root node of a tree.

Many methods on the text model side do state they are thread-safe. Some inner classes in javax.swing.text.html.StyleSheet, however, explicitly state they are not thread-safe. Again, the above applies: Running outside the event-thread is ok, as long as there is additional synchronization.

Share

About Mathias

Software development engineer. Principal developer of DrJava. Recent Ph.D. graduate from the Department of Computer Science at Rice University.
This entry was posted in Concurrent Unit Testing. Bookmark the permalink.

Leave a Reply