views:

125

answers:

3

After the introduction of Java Memory Model, the Swing guidelines were changed to state that any Swing components need to be instantiated on the EDT in order to avoid non-published instance state.

What I could not find anywhere is whether the classloading is also mandated to be on the EDT or can we pre-load key Swing classes in a background thread? Is there any official statement from Sun/Oracle on this? Are there any classes that are known to hold non-threadsafe static state, hence need to be loaded on EDT?

Clarification to address Nemi's question: this is a practical issue. A sizable portion of our application's startup time, is spent classloading, and font/image-loading on the EDT. Most of this can be attributed to Swing and related libraries.

Here is som background: As many other Swing apps, on startup we are pre-constructing many forms, in order to make the UI more responsive. After profiling, we found that the actual time for form construction is relatively fast - what's slow is loading of all classes and fonts (disk reads are slow in corporate setup with on-access virus scanner, surveilance scanner, audit tracker and god knows what else tacked on the HDD driver).

We tried to construct the same forms in a background thread (violating Swing's rules) and then throw them away. Once we are done we construct the same forms on the EDT, which is much faster as all classes are loaded and any other files are in the disk cache. It works for us, and we'll probably keep doing it unless something really bad happens.

What I'm asking is whether this is a safe practice, a good practice or a hack?

A: 

Although you are technically correct--I've never heard of a problem with rendering the forms in a different thread as long as you don't do anything with them once they are realized except with the EDT (as per sun's original guidelines).

Those used to be sun's guidelines but Sun changed them to be as you specified--so I'm sure someone found or created a potential conflict, but I'm also sure it's going to be damn hard to hit since apps still work and almost none follow those guidelines.

As to your question, I'm sure you can load the classes as long as you don't instantiate any objects and still be within even the strictest guidelines.

The EDT is only used to avoid thread collisions because Swing is single threaded (by design). If you are only loading the classes and not instantiating any, you aren't opening yourself up for any threading issues.

Bill K
The thing that worries me is the static state. The EDT rule is solving the atomicity problem, but doesn't take care of visibility - setting a field to a value in one thread is not guaranteed to be visible from another thread unless proper publication is done (e.g. by synchronization, CAS, etc). Note that right now, the chance is prety high that two threads will be scheduled on the same CPU. With the advent of multicore systems, the problem will get significant.
ddimitrov
@ddimitrov The thing is that until it's realized, there is extremely little chance that any other thread will have access to it. Now, there must be SOME chance, but simply loading the classes cannot cause a problem because no code is running inside the classes yet. I'm not sure what (if anything) you are disagreeing with. When you actually instantiate it--and forever after--you should only use the EDT, that just doesn't imply that the EDT must be used to load the classes which was the OP's question.
Bill K
When you load the classes any static initializers run in the loading thread and any non-final static state is subject to safe-publication rules. I believe there's no much mutable static state in Swing, but (as I gave examples) there is some. Otherwise I agree that the chances for a problem are low, but when there is a problem it will be impossible to diagnose or even reproduce reliably.
ddimitrov
+2  A: 

It is safe. See: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html (the single thread rule)

If you are afraid or want some feedback / debug, have a look at this FEST/Swing tool: http://fest.easytesting.org/swing/wiki/pmwiki.php?n=FEST-Swing.EDT ("Testing that access to GUI components is done in the EDT") -- it is a custom RepaintManager that fails when you violate the EDT access policy.

You may find this helpful as well: http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html

They don't mention class loading explicitly, but they do explain what the EDT access policy is about.

Konrad Garus
Again I am not talking about EDT policy violations here. The EDT policy is not an arbitrary rule, it's there for a reason and I have the feeling that it's not complete. See mdma's answer for more details.
ddimitrov
+1  A: 

The evidence seems to suggest it is safe - but then again, as ddimitrov says in the comments - the odds are in favor of not finding subtle thread bugs due to non-published changes because typical machines have only a few cores, and L2/L3 caches are shared. (L1 caches are per core, but usally very small.)

If you want to guarantee no problem arises because of the background classloading, then it's probably safest to stick to loading classes on the ETD. To maintain a live UI, create a custom class loader that also pumps events in-between loading each class. (Dependencies are loaded re-entrantly so the delay would only be for the duration of loading just one class.) Assuming this class loader is packaged with your application, then it can simply defer all class loading to it's classloader.

Alternatively, Secondary event queues can be spanwed that run on separate threads (e.g. modal dialogs, and the spin library). This implies Swing can run on any thread, as long as it runs on just one, and means that it must be update-consistent (or that we've all just been very lucky so far!) On the basis of this, you could load your classes on the primary EDT, and start a secondary EDT to pump the UI events, keeping the UI responsive - in the same way a modal dialog functions. The Spin utility will pump EDT events for you, or you can spawn a new EDT by hand.

mdma
Thanks, this answers my question.
ddimitrov
Thinking about it, running Swing on a brand new EDT spawned from the old is safe, because Thread.start() is a safe publication. Then in order to see the changes we need to either Thread.join() the temporary dispatcher or spawn yet another dispatcher (that will see the newly loaded classes) and kill the old EDT. Sounds messy...
ddimitrov
If you use the Spin library the details are taken care of. I don't think it's quite as messy as you fear - the secondary EDT can be terminated by posting an event to it. You do this, and the Thread.join() after loading all the classes.
mdma