Взаимно рекурсивно определенные статические поля вызывают замораживание программы, но не без потоков и не с gcj?


Вот простой код:

class B {final static int x = C.x;}
class C {final static int x = B.x;}
class A {
    static {
        System.out.println("A static{}");
        new Thread() { public void run() { new B(); }}.start();
        new Thread() { public void run() { new C(); }}.start();
    }
    public static void main(String[] args) {
        System.out.println("A main");
        System.out.println("B.x: " + B.x);
        System.out.println("C.x: " + C.x);
    }
}

B.x и C.x определяются в терминах друг друга. Я думал, что это не должно компилироваться, но это так.

Он зависает в main, когда я пытаюсь запустить его:

$ javac *.java && java A
A static{}
A main

Почему?

Тем не менее, он прекрасно работает в gcj:

$ gcj --main=A -o a *.java && ./a
A static{}
A main
B.x: 0
C.x: 0

Почему?

Также, если я избавлюсь от нитей,

class B {final static int x = C.x;}
class C {final static int x = B.x;}
class A {
    static {
        System.out.println("A static{}");
        new B(); 
        new C(); 
    }
    public static void main(String[] args) {
        System.out.println("A main");
        System.out.println("B.x: " + B.x);
        System.out.println("C.x: " + C.x);
    }
}

Он отлично работает как в java, так и в gcj:

$ javac *.java && java A
A static{}
A main
B.x: 0
C.x: 0
$ gcj --main=A -o a *.java && ./a
A static{}
A main
B.x: 0
C.x: 0

И все переменные имеют значение 0. Почему? Не должно ли это не компилироваться, так как переменные являются static final и никогда нигде не назначаются?

1 3

1 ответ:

Вы создаете условие взаимоблокировки, которое может быть или не быть проблемой в зависимости от того, как быстро вы запускаете свои потоки.

При первом использовании класса он инициализирует класс и вызывает статические блоки. Он делает это потокобезопасным способом, и ни один другой поток не может получить доступ к классу, пока это не будет сделано.

У вас есть два потока, и в случае взаимоблокировки один имеет класс B и хочет класс C, другой имеет класс C и хочет класс B.

Как это довольно быстро, один поток может выполняться до завершения, прежде чем начнется другой, и в этом случае взаимоблокировки не происходит.

Не должно ли это не компилироваться, так как переменные являются статическими конечными и никогда нигде не назначаются?

Вы присвоили значение, однако вы получаете доступ к значению, пока оно инициализируется, поэтому вы видите значение по умолчанию 0.