Class initialization deadlock mechanism explanation

旧巷老猫 提交于 2019-12-12 01:19:08

问题


I found article(https://habr.com/company/odnoklassniki/blog/255067/) from the @apangin(https://stackoverflow.com/users/3448419/apangin) on my native langugage but I am not able to understand it. Explanation is very concise

Let's consider that code:

static class A {
    static final B b = new B();
}

static class B {
    static final A a = new A();
}

public static void main(String[] args) {
    new Thread(A::new).start();
    new B();
}

You could try to run that code. On my pc it leads to deadlockwith 75% probability/

So we have 2 threads:

Thread_1 is creating instance of A

Thread_2(main thread) is creating instance of B

It is first access to class so it leads(might lead) to concurrent classes A and B initialization.

Next step is not clear for me. Could you explain it?


回答1:


JVM Specification §5.5 describes class initialization procedure in detail. It consists of 12 steps.

At step 6 the class is marked as "in progress of initialization by the current thread".

  1. Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC.

So Thread_1 starts initializing class A and marks it as being initialized by Thread_1. Similarly class B is marked as being initialized by Thread_2.

At step 9 the static initializer is invoked.

  1. Next, execute the class or interface initialization method of C.

The static initializer of A creates an instance of B, which is not yet fully initialized, so Thread_1 (while in progress of initializing A) recursively starts the procedure of initializing B.

At step 2 of the procedure it detects that class B is in progress of initialization by different thread and blocks.

  1. If the Class object for C indicates that initialization is in progress for C by some other thread, then release LC and block the current thread until informed that the in-progress initialization has completed, at which time repeat this procedure.

Symmetrically Thread_2 starts initialization of A, detects that it is already being initialized by different thread and also blocks at step 2. Both threads are blocked waiting for each other.

Note: the class is marked as fully initialized and notifies other threads after successful invocation of static initializer, which never happens in this case.

  1. If the execution of the class or interface initialization method completes normally, then acquire LC, label the Class object for C as fully initialized, notify all waiting threads, release LC, and complete this procedure normally.



回答2:


Thread_1 is creating instance of A

Yes. And the new keyword requires the class A to be initialized.

Thread_2(main thread) is creating instance of B

Yes. And the new keyword also requires the class B to be initialized.

But there is a dependency between these classes in the clinit (static blocks and static fields initialization).


From JLS. 12.4.2. Detailed Initialization Procedure:

For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation. The procedure for initializing C is then as follows:

  1. Synchronize on the initialization lock, LC, for C. This involves waiting until the current thread can acquire LC.

(Other steps are omitted, but the first is the most important one)

So here's what might happen:

Thread_1:

  1. Sees that it should create an instance of A class (new)
  2. Begins class A initialization (first A class usage)
  3. Acquires initialization lock of the A class.

(Here the OS thread scheduler decides that it's time to suspend the Thread_1 and give some time to main thread)

And the main thread:

  1. Sees that it should create an instance of B class (new)
  2. Begins class B initialization (first B class usage)
  3. Acquires initialization lock of the B class.

The os thread scheduler now suspends thread main and gives some time to Thread_1.

The Thread_1 continues:

  1. It sees static final B b = new B();
  2. It is not allowed to create a new instance of B, because the B class is not initialized yet.
  3. It tries to acquire initialization lock of the B class, but the lock is held by the main thread.

Thread scheduler again suspends Thread_1 and gives some time to main. And it continues:

  1. It sees static final A a = new A();
  2. It is not allowed to create a new instance of A, because the B class is not initialized yet.
  3. It tries to acquire initialization lock of the A class, but the lock is held by Thread_1 thread.

So here is a deadlock:

  1. The Thread_1 holds A initialization lock and wants to acquire the B class initialization lock
  2. The main thread holds B initialization lock and wants to acquire the A class initialization lock


来源:https://stackoverflow.com/questions/53682182/class-initialization-deadlock-mechanism-explanation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!