在 java 代码中出现死锁
Getting deadlock in java code
我有以下三个类。
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 方法时,我得到以下输出 -
calling static method of derived class..
In load method of base class
此执行停止后,但进程仍在 运行。
所以似乎有一些僵局,但我不明白为什么会这样。
需要帮助。
当第一次引用 BaseClass
时,class 加载程序启动并想要设置 class 以供使用。所以它加载 class 并启动静态初始化块
static {
load();
}
这会调用 load
方法,然后您会尝试创建类型为 DerivedClass
的对象。这将首先尝试调用 super()
构造函数,即 class BaseClass
的方法 - 但 BaseClass
尚未完全初始化,因为它的静态初始化程序尚未完成 = > 僵局。
编辑:
根据您的评论,我做了更多的研究。其实,事情并没有我想的那么简单。 JVM 能够处理递归初始化,因此在单线程情况下没有问题。 class-initialization-process 的描述可以在 JVM 规范的第 5.5 节中找到。
这里的罪魁祸首实际上是两个初始化进程之间的竞争条件。
线程1到达DerivedClass.isSynthetic("test")
,开始初始化DerivedClass
。
同时线程 2 到达 BaseClass.init()
并开始初始化 BaseClass
。
在初始化 DerivedClass
时,线程 1 认识到它必须初始化 superclass。由于线程 2 已经在进行 BaseClass
的初始化,因此线程 1 必须等待它完成。
初始化时 BaseClass
线程 2 到达 DerivedClass dc = new DerivedClass();
。由于 DerivedClass
的初始化已经由线程 1 进行,线程 2 必须等待它完成。
所以实际上这是一个 class 逻辑死锁,其中两个线程尝试以不同的顺序(BaseClass->DerivedClass 与 DerivedClass->BaseClass)进入两个关键代码路径("initialization of class X")并结束起来等待对方。
在适当的地方添加一些 Thread.sleep(100);
也会告诉你这确实是一个竞争条件。在我的测试中,有时程序会成功完成,尽管初始化期间存在循环依赖。
我有以下三个类。
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 方法时,我得到以下输出 -
calling static method of derived class..
In load method of base class
此执行停止后,但进程仍在 运行。 所以似乎有一些僵局,但我不明白为什么会这样。 需要帮助。
当第一次引用 BaseClass
时,class 加载程序启动并想要设置 class 以供使用。所以它加载 class 并启动静态初始化块
static {
load();
}
这会调用 load
方法,然后您会尝试创建类型为 DerivedClass
的对象。这将首先尝试调用 super()
构造函数,即 class BaseClass
的方法 - 但 BaseClass
尚未完全初始化,因为它的静态初始化程序尚未完成 = > 僵局。
编辑: 根据您的评论,我做了更多的研究。其实,事情并没有我想的那么简单。 JVM 能够处理递归初始化,因此在单线程情况下没有问题。 class-initialization-process 的描述可以在 JVM 规范的第 5.5 节中找到。
这里的罪魁祸首实际上是两个初始化进程之间的竞争条件。
线程1到达DerivedClass.isSynthetic("test")
,开始初始化DerivedClass
。
同时线程 2 到达 BaseClass.init()
并开始初始化 BaseClass
。
在初始化 DerivedClass
时,线程 1 认识到它必须初始化 superclass。由于线程 2 已经在进行 BaseClass
的初始化,因此线程 1 必须等待它完成。
初始化时 BaseClass
线程 2 到达 DerivedClass dc = new DerivedClass();
。由于 DerivedClass
的初始化已经由线程 1 进行,线程 2 必须等待它完成。
所以实际上这是一个 class 逻辑死锁,其中两个线程尝试以不同的顺序(BaseClass->DerivedClass 与 DerivedClass->BaseClass)进入两个关键代码路径("initialization of class X")并结束起来等待对方。
在适当的地方添加一些 Thread.sleep(100);
也会告诉你这确实是一个竞争条件。在我的测试中,有时程序会成功完成,尽管初始化期间存在循环依赖。