遇到一个没想明白的问题。
一个类的静态字段是当前类的子类,多线程同时实例化该类和子类时,线程卡住。
有一个类DbWhere,它的一个静态字段NONE是DbWhere自身的子类。
定义成这个结构,是想实现避免出现预期之外的条件为空:
1、直接new DbWhere(),则条件不允许完全为空;
2、如果预知条件为空,就要用DbWhere.NONE;
3、如果有可能为空,则使用new EmptiableWhere()。
目前已经通过删除EmptiableWhere,把emptiable作为DbWhere的一个字段,绕过了这个问题。
但是造成问题的原理不清楚,记录一下。
问题可以重现,不是必现,概率80%以上。
git clone https://gitee.com/qdbp/db-where-test.git
cd db-where-test/code
test.bat
DbWhere简化之后如下:
public class DbWhere {
/** 没有查询条件的空Where **/
public static final DbWhere NONE = new ReadonlyWhere();
public DbWhere() {
log(this.getClass(), "DbWhere<init>()");
}
static void log(Class<?> clazz, String msg) {
System.out.println(Thread.currentThread().getName() + ' ' + clazz.getSimpleName() + '-' + msg);
}
}
/** 允许为空的查询条件 **/
public class EmptiableWhere extends DbWhere {
public EmptiableWhere() {
log(this.getClass(), "DbWhere<init>()");
}
}
/** 只读查询条件 **/
public class ReadonlyWhere extends EmptiableWhere {
public ReadonlyWhere() {
log(this.getClass(), "DbWhere<init>()");
}
}
两个测试线程类
public class DbWhereThread extends Thread {
public void run() {
new DbWhere();
}
}
public class EmptiableWhereThread extends Thread {
public void run() {
new EmptiableWhere();
}
}
测试1,多线程实例化父类DbWhere,没有问题
public class DbWhereTest1 {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new DbWhereThread().start();
}
}
}
测试2,多线程实例化子类EmptiableWhere,没有问题
public class DbWhereTest2 {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new EmptiableWhereThread().start();
}
}
}
测试3,先实例化DbWhere,再多线程同时实例化父类和子类,没有问题
public class DbWhereTest3 {
public static void main(String[] args) {
new DbWhere();
for (int i = 0; i < 3; i++) {
new DbWhereThread().start();
new EmptiableWhereThread().start();
}
}
}
测试4,多线程同时实例化父类和子类,线程卡住了
public class DbWhereTest4 {
public static void main(String[] args) {
System.out.println("DbWhereTest4");
for (int i = 0; i < 3; i++) {
new DbWhereThread().start();
new EmptiableWhereThread().start();
}
}
}
从线程Dump看,都卡在构造函数这里
"Thread-5" #16 prio=5 os_prio=0 tid=0x00000000226f0000 nid=0x6284 in Object.wait() [0x000000002477f000]
java.lang.Thread.State: RUNNABLE
at EmptiableWhereThread.run(EmptiableWhereThread.java:3)
Locked ownable synchronizers:
- None
"Thread-4" #15 prio=5 os_prio=0 tid=0x00000000226ed800 nid=0x5d20 in Object.wait() [0x000000002467f000]
java.lang.Thread.State: RUNNABLE
at DbWhereThread.run(DbWhereThread.java:3)
Locked ownable synchronizers:
- None
"Thread-3" #14 prio=5 os_prio=0 tid=0x00000000226ec800 nid=0x4640 in Object.wait() [0x000000002457f000]
java.lang.Thread.State: RUNNABLE
at EmptiableWhereThread.run(EmptiableWhereThread.java:3)
Locked ownable synchronizers:
- None
"Thread-2" #13 prio=5 os_prio=0 tid=0x00000000226e8000 nid=0x409c in Object.wait() [0x000000002447f000]
java.lang.Thread.State: RUNNABLE
at DbWhereThread.run(DbWhereThread.java:3)
Locked ownable synchronizers:
- None
"Thread-1" #12 prio=5 os_prio=0 tid=0x00000000226e5800 nid=0x6760 in Object.wait() [0x000000002437e000]
java.lang.Thread.State: RUNNABLE
at EmptiableWhereThread.run(EmptiableWhereThread.java:3)
Locked ownable synchronizers:
- None
"Thread-0" #11 prio=5 os_prio=0 tid=0x00000000226e2000 nid=0x62c8 in Object.wait() [0x000000002427e000]
java.lang.Thread.State: RUNNABLE
at DbWhere.<clinit>(DbWhere.java:4)
at DbWhereThread.run(DbWhereThread.java:3)
Locked ownable synchronizers:
- None