我有三节课。
BaseClass.java
public class BaseClass {
static {
load();
}
public static void init() {
System.out.println("base init");
}
private static void load() {
System.out.println("In load method of base class");
DerivedClass dc = new DerivedClass();
System.out.println("Object creation done.");
}
}
DerivedClass.java
public class DerivedClass extends BaseClass {
public DerivedClass() {
System.out.println("derived class constructor");
}
public static boolean isSynthetic(String _attr) {
return true;
}
}
Helper.java
public class Helper {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
BaseClass.init();
};
};
t.start();
System.out.println("calling static method of derived class..");
System.out.println(DerivedClass.isSynthetic("test"));
}
}
当我从Helper.java执行main方法时,我得到以下输出 -
调用派生类的静态方法..
在基类的加载方法
中
此操作暂停后但进程仍在运行。 所以似乎有一些僵局,但我不明白为什么会这样。 需要帮助。
答案 0 :(得分:2)
当第一次引用BaseClass
时,类加载器会启动并希望设置该类以供使用。所以它加载类并启动静态初始化程序块
static {
load();
}
这会调用load
- 方法,并尝试创建DerivedClass
类型的对象。这将首先尝试调用super()
- 构造函数,即类BaseClass
的方法 - 但BaseClass
尚未完全初始化,因为其静态初始化程序尚未完成=>死锁。
修改强> 根据你的评论我做了一些更多的研究。实际上,事情并不像我想象的那么简单。 JVM能够处理递归初始化,因此单线程情况下没有问题。类初始化过程的描述可以在JVM规范的5.5节中找到。
这里的罪魁祸首是两个初始化过程之间的竞争条件。
线程1到达DerivedClass.isSynthetic("test")
,并开始DerivedClass
的初始化。
同时,线程2到达BaseClass.init()
并开始BaseClass
的初始化。
初始化DerivedClass
线程1时识别它必须初始化超类。由于线程2线程1已经在进行BaseClass
的初始化,因此线程1必须等待它完成。
初始化BaseClass
主题2达到DerivedClass dc = new DerivedClass();
。由于线程1线程2已经在进行DerivedClass
的初始化,因此线程2必须等待它完成。
所以实际上这是一个经典的死锁,其中两个线程试图以不同的顺序(BaseClass-> DerivedClass vs. DerivedClass-> BaseClass)输入两个关键的代码路径(“类X的初始化”)并最终等待每个其他
在适当的位置添加一些Thread.sleep(100);
也会向您显示这确实是一种竞争条件。在我的测试期间,有时程序成功完成,尽管初始化期间存在循环依赖。