我有以下三个类(class)。

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方法时,我得到以下输出-



执行停止后,进程仍在运行。
因此似乎有一些僵局,但我不明白为什么会这样。
需要帮助。

最佳答案

当第一次引用BaseClass时,类加载器将启动并希望设置要使用的类。因此,它将加载该类并启动静态初始化程序块

static {
    load();
}

这将调用load -method,然后您尝试在其中创建DerivedClass类型的对象。这将首先尝试调用super() -constructor,即BaseClass类的方法-但BaseClass尚未完全初始化,因为其静态初始值设定项尚未完成=>死锁。

编辑:
根据您的评论,我进行了更多研究。实际上,事情并没有我想象的那么简单。 JVM能够处理递归初始化,因此在单线程情况下没有问题。可以在JVM规范的第5.5节中找到有关类初始化过程的描述。

真正的罪魁祸首实际上是两个初始化过程之间的竞争条件。

线程1到达DerivedClass.isSynthetic("test"),并开始DerivedClass的初始化。

同时,线程2到达BaseClass.init()并开始BaseClass的初始化。

在初始化DerivedClass线程1时,它认识到必须初始化父类(super class)。由于线程2已在进行BaseClass的初始化,因此线程1必须等待其完成。

在初始化BaseClass时,线程2到达DerivedClass dc = new DerivedClass();。由于线程1已在进行DerivedClass的初始化,因此线程2必须等待其完成。

因此,实际上这是一个经典的死锁,其中两个线程尝试以不同的顺序(BaseClass-> DerivedClass与DerivedClass-> BaseClass)输入两个关键代码路径(“类X的初始化”),并最终互相等待。

在适当的位置添加一些Thread.sleep(100);也将向您表明这确实是一个竞争条件。在我的测试过程中,尽管初始化期间存在循环依赖性,但有时程序还是成功完成了。

关于java - 在Java代码中陷入僵局,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37959532/

10-10 22:25