遇到一个没想明白的问题。
一个类的静态字段是当前类的子类,多线程同时实例化该类和子类时,线程卡住。
有一个类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: RUNNABLEat 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: RUNNABLEat 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: RUNNABLEat 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: RUNNABLEat 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: RUNNABLEat 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: RUNNABLEat DbWhere.<clinit>(DbWhere.java:4)at DbWhereThread.run(DbWhereThread.java:3)Locked ownable synchronizers:- None
