Deep inside the library that I use, there are two mutually referential classes β call them MR1 and MR2 β so that MR1.<clinit> causes MR2 and MR2.<clinit> load MR2.<clinit> causes MR1 load.
The library also has a Util class that references MR1 , so Util.<clinit> calls MR1 load.
Finally, it has a top-level class β call it TopLevel β whose constructor has the following code:
Util.callStaticUtilMethod(); MR2.callStaticMR2Method();
Thus, the overall dependency graph looks something like this:

Recently, I ran into a dead end class loader in which two threads simultaneously instantiated TopLevel at the same time that none of these other classes had been loaded yet. One thread stuck in MR1.<clinit> , inside Util.<clinit> , in preparation for calling Util.callStaticUtilMethod() ; another successfully passed Util.callStaticUtilMethod() and got stuck in MR2.<clinit> , getting ready to call MR2.callStaticMR2Method() . (And then many other threads got stuck in the line MR2.callStaticMR2Method() .)
What I do not understand is & mdash; if one thread is still stuck, deadlocked inside Util.<clinit> , then how did all the other threads get past the call to Util.callStaticUtilMethod() ? Is the class supposed to be used before it is fully initialized? If so, how far does it go; for example, can access a static final field by one thread before it is initialized by another? (Due to the fact that other threads could not get through the call of MR2.callStaticMR2Method() , it seems that this, fortunately, is not complete freedom for everyone, but I canβt say what the rules may be.)
source share