Was the class used (in one thread) before being fully initialized (in another)?

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:

dependency graph

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.)

+5
source share
1 answer

JLS ( Β§12.4.1 ) indicates that a call to a static method initiates initialization:

A class or interface type T will be initialized just before ... the static method declared by T is called.

And this continues ( Β§12.4.2 ) to describe what should happen when a thread initiates initialization:

If ... initialization is done in C by another thread, then ... block the current thread until you indicate that the process has initialized. Otherwise, write down the fact that the class initialization of the object for C is performed by the current thread ... If the initializers complete normally ... mark the class object for C as fully initialized, [and] will notify all pending threads.

So theoretically, what you are describing is impossible. However, there is a somewhat vague footnote:

Compile-time analysis can in some cases eliminate many of the checks that the type initialized from the generated code .... Such an analysis should, however, fully take into account concurrency, but for the fact that the initialization code is unlimited.

Which, as I interpret it, could give the compiler the freedom to avoid blocking calls to static methods that do not rely on any initialization state or otherwise affect the memory model.

However, I tried to reproduce the behavior (of the static method returned before the class was initialized), and could not do it. You should not exclude that you simply do not read your magazines.

0
source

Source: https://habr.com/ru/post/1235159/


All Articles