问题
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".
- 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.
- 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.
- 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.
- 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:
- 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:
- Sees that it should create an instance of
Aclass (new) - Begins class
Ainitialization (firstAclass usage) - Acquires initialization lock of the
Aclass.
(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:
- Sees that it should create an instance of
Bclass (new) - Begins class
Binitialization (firstBclass usage) - Acquires initialization lock of the
Bclass.
The os thread scheduler now suspends thread main and gives some time to Thread_1.
The Thread_1 continues:
- It sees
static final B b = new B(); - It is not allowed to create a new instance of
B, because theBclass is not initialized yet. - It tries to acquire initialization lock of the
Bclass, but the lock is held by themainthread.
Thread scheduler again suspends Thread_1 and gives some time to main. And it continues:
- It sees
static final A a = new A(); - It is not allowed to create a new instance of
A, because theBclass is not initialized yet. - It tries to acquire initialization lock of the
Aclass, but the lock is held byThread_1thread.
So here is a deadlock:
- The
Thread_1holdsAinitialization lock and wants to acquire theBclass initialization lock - The
mainthread holdsBinitialization lock and wants to acquire theAclass initialization lock
来源:https://stackoverflow.com/questions/53682182/class-initialization-deadlock-mechanism-explanation