在完全初始化(由另一个线程)之前使用的类(在一个线程中)?

rua*_*akh 5 java multithreading classloader

我使用的是库内心深处,有两个相互引用的类-叫他们MR1MR2-这样MR1.<clinit>会导致MR2被classloaded并MR2.<clinit>导致MR1被classloaded.

该库还具有Util类指MR1的是,例如Util.<clinit>使MR1被classloaded.

最后,它有一个顶级类 - 调用它TopLevel- 其构造函数有一些代码如下:

Util.callStaticUtilMethod();
MR2.callStaticMR2Method();
Run Code Online (Sandbox Code Playgroud)

所以整体依赖图是这样的:

依赖图

我最近遇到了一个类加载器死锁,其中两个线程碰巧同时实例化TopLevel,而其他类都没有被加载.一个线程卡在MR1.<clinit>里面Util.<clinit>,准备呼叫Util.callStaticUtilMethod(); 另一个成功地过去了Util.callStaticUtilMethod(),并且被困住了MR2.<clinit>,准备打电话MR2.callStaticMR2Method().(然后一堆其他线程卡在了MR2.callStaticMR2Method()线上.)

我不明白的是 - 如果一个线程仍然卡住,死锁,内部Util.<clinit>,那么所有其他线程如何能够通过Util.callStaticUtilMethod()呼叫?是否可以在类完全初始化之前使用它?如果是这样,那么这会走多远; 例如,一个线程在被另一个线程初始化之前是否可以访问静态最终字段?(从其他线程无法通过MR2.callStaticMR2Method()呼叫的事实来看,似乎这并不是一个完全免费的,幸运的是;但我不知道规则可能是什么.)

dim*_*414 0

JLS ( \xc2\xa712.4.1 ) 声明静态方法调用将触发初始化:

\n\n
\n

类或接口类型 T 将在调用 T 声明的静态方法之前立即初始化。

\n
\n\n

它继续(\xc2\xa712.4.2)描述当线程触发初始化时应该发生什么:

\n\n
\n

如果...其他线程正在对 C 进行初始化,则...阻塞当前线程,直到通知正在进行的初始化已完成...否则,记录 C 的 Class 对象的初始化这一事实当前线程正在进行中...如果初始化程序的执行正常完成...将 C 的 Class 对象标记为完全初始化,[并]通知所有等待线程。

\n
\n\n

所以理论上你所描述的情况是不可能的。然而,有一个有点模糊的脚注:

\n\n
\n

在某些情况下,编译时分析可能​​能够消除对已从生成的代码中初始化类型的许多检查......但是,此类分析必须充分考虑并发性以及初始化代码是这样的事实:不受限制。

\n
\n\n

据我解释,这可以让编译器自由地避免阻塞不依赖于任何仍在初始化状态或以其他方式影响内存模型的静态方法调用。

\n\n

也就是说,我尝试复制该行为(静态方法在其类的初始值设定项执行之前返回),但未能做到。您不应该排除您只是误读日志的可能性。

\n