This is an update to the following blog posts:
Easily Find Swing Threading Issues - Part I
Easily Find Swing Threading Issues - Part II
I got an official comment on this issue from Scott Violet, JFC/Swing Lead at Sun:
-
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.
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.
There is also more recent information from Sun than the article I previously linked to:
So where does that all leave us? Well it looks as if the new guideline is to not create things off the EDT. If you do create things definitely don't realize them or modify them off the EDT.
Now on to a couple comments on the ThreadCheckingRepaintManager. I have uploaded a new version. With this version I have added the ability to specifiy an isShowing() check. So if you follow the "everything on the EDT thread rule", don't allow nonrealized components to be accessed outside the EDT thread by turning the check off. If you do thread things, you can turn it on to minimize output. I've also added the dump of the component tree with visibility settings when a "violation" is found. Sometimes this is useful when you are adding components to containers that you thought were not visible yet.
Finally, lets talk about the thread safe methods of repaint(), revalidate(), and invalidate(). I've added an example in the ThreadCheckingRepaintManager source of calling those methods off the EDT. The revalidate() method is wrapped in SwingUtilities.invokeLater() if called off the EDT so it is ok. The invalidate() method seems to call some layout manager voodoo. If you look at repaint() in Component it delegates down to a repaint(long tm, int x, int y, int width, int height). This method queues Paint events on the Event Queue eventually through a synchronized method. JComponent overrides this repaint method and adds dirty regions to the RepaintManager. This call is synchronized as well.
The repaint() method causes problems for the ThreadCheckingRepaintManager that I'm trying to figure out. Calling repaint directly from a non EDT thread is ok. However, I'm pretty sure as one blog comment pointed out there is code in Swing components that call repaint that isn't thread safe. So you end of with a stack of methods getting to the ThreadCheckingRepaint manager that goes through a thread safe method but isn't really thread safe. I've thought about parsing the stacktrace but that won't solve the problem above. For the moment I'm just going to leave repaint calls in as being flagged.
If anyone has anymore suggestions please let me know. The comments thus far have been very useful. I don't claim that this tool finds every problem or handles every case like the repaint() method. I'm hopeful thought is it a useful tool in identifying a good amount of Swing related threading issues that people have in their code.
----------Wrong Thread START java.lang.Exception at dk.ifad.gui.ThreadCheckingRepaintManager.checkThread(ThreadCheckingRepaintManager.java:29) at dk.ifad.gui.ThreadCheckingRepaintManager.addDirtyRegion(ThreadCheckingRepaintManager.java:52) at javax.swing.JComponent.repaint(JComponent.java:4485) at java.awt.Component.imageUpdate(Component.java:2939) at javax.swing.JLabel.imageUpdate(JLabel.java:874) at sun.awt.image.ImageWatched$WeakLink.newInfo(ImageWatched.java:114) at sun.awt.image.ImageWatched.newInfo(ImageWatched.java:151) at sun.awt.image.ImageRepresentation.setPixels(ImageRepresentation.java:454) at sun.awt.image.ImageDecoder.setPixels(ImageDecoder.java:108) at sun.awt.image.GifImageDecoder.sendPixels(GifImageDecoder.java:428) at sun.awt.image.GifImageDecoder.parseImage(Native Method) at sun.awt.image.GifImageDecoder.readImage(GifImageDecoder.java:551) at sun.awt.image.GifImageDecoder.produceImage(GifImageDecoder.java:195) at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.java:246) at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:172) at sun.awt.image.ImageFetcher.run(ImageFetcher.java:136)I use Java 1.5.0_03
Add a comment



