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

时间:2015-11-04 01:36:42

标签: java multithreading classloader

在我正在使用的图书馆深处,有两个相互引用的类 - 称之为MR1MR2 - 这样MR1.<clinit>会导致MR2成为MR2.<clinit> classloaded和MR1导致Util被分类。

该库还有一个引用MR1的{​​{1}}类,因此Util.<clinit>会导致MR1被分类。

最后,它有一个顶级类 - 称之为TopLevel - 其构造函数有如下代码:

Util.callStaticUtilMethod();
MR2.callStaticMR2Method();

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

dependency graph

我最近遇到了一个类加载器死锁,其中两个线程碰巧同时实例化了TopLevel,而其他类都没有被加载。一个帖子卡在MR1.<clinit>Util.<clinit>内,准备调用Util.callStaticUtilMethod();另一个成功通过Util.callStaticUtilMethod(),并被卡在MR2.<clinit>,准备调用MR2.callStaticMR2Method()。 (然后一堆其他线程卡在MR2.callStaticMR2Method()线上。)

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

1 个答案:

答案 0 :(得分:0)

JLS(§12.4.1)声明静态方法调用将触发初始化:

  

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

继续(§12.4.2)来描述线程触发初始化时会发生什么:

  

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

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

  

在某些情况下,编译时分析可以消除许多已从生成的代码初始化类型的检查。但是,这种分析必须完全考虑并发性和初始化代码不受限制的事实。

正如我所解释的那样,它可以让编译器自由地避免阻止静态方法调用,这些调用不依赖于任何静止初始化状态或以其他方式影响内存模型。

那就是说,我试图复制行为(在类初始化程序执行之前返回的静态方法)并且还没有能够这样做。你不应该排除你错过阅读日志的可能性。