Qestion
/**
* ClassInitializedOrder for : Java Classload Order Test
*
* @author <a href="mailto:[email protected]">Isaac.Zhang | 若初</a>
* @since 2019/7/20
*/
// CASE 1
public class ClassInitializedOrder {
private static boolean initialized = false;
static {
println("static 代码块执行。");
Thread thread = new Thread(() -> initialized = true);
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
println("main 函数执行。");
System.out.println("initialized = " + initialized);
}
private static void println(Object o){
System.out.println(o);
}
}
-------------------------------------------------------------------
// CASE 2
public class ClassInitializedOrder {
private static boolean initialized = false;
static {
println("static 代码块执行。");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
println("Runnable 代码块执行。");
initialized = true;
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
println("main 函数执行。");
System.out.println("initialized = " + initialized);
}
private static void println(Object o){
System.out.println(o);
}
Answer
- A:
initialized = true
- B:
initialized = false
- C: Compile Error
- D: these answers are wrong
Explain
Program execution, App Classloader will first load ClassInitializedOrder.class
, are executed in the order of classes.
private static boolean initialized = false;
CASE 1
We all know that static
the block will be initialized when the class is loaded, the next step will be to perform Thread thread = new Thread(() -> initialized = true);
our first look at the byte code of the current line:
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=3, locals=2, args_size=0
0: iconst_0
1: putstatic #7 // Field initialized:Z
4: new #11 // class java/lang/Thread
7: dup
8: invokedynamic #12, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
13: invokespecial #13 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
16: astore_0
17: aload_0
18: invokevirtual #14 // Method java/lang/Thread.start:()V
21: aload_0
22: invokevirtual #15 // Method java/lang/Thread.join:()V
25: goto 33
28: astore_1
29: aload_1
30: invokevirtual #17 // Method java/lang/InterruptedException.printStackTrace:()V
33: return
Analysis #12
can be seen that the processing needs of the current row ()
is changed to handle anonymous class itself, InvokeDynamic
but also depends on the current which the main class of the current instruction execution, does not end the execution of the main class, the main class so it needs to wait for the end of execution, So will this stop, as follows:
CASE 2
Continue to view bytecode:
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=4, locals=2, args_size=0
0: iconst_0
1: putstatic #1 // Field initialized:Z
4: ldc #14 // String static 代码块执行。
6: invokestatic #2 // Method println:(Ljava/lang/Object;)V
9: new #15 // class java/lang/Thread
12: dup
13: new #16 // class com/sxzhongf/daily/question/july/ClassInitializedOrder$1
16: dup
17: invokespecial #17 // Method com/sxzhongf/daily/question/july/ClassInitializedOrder$1."<init>":()V
20: invokespecial #18 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
23: astore_0
24: aload_0
25: invokevirtual #19 // Method java/lang/Thread.start:()V
28: aload_0
29: invokevirtual #20 // Method java/lang/Thread.join:()V
32: goto 40
35: astore_1
36: aload_1
37: invokevirtual #22 // Method java/lang/InterruptedException.printStackTrace:()V
40: return
View #16
, we can see here becomes new #16 // class com/sxzhongf/daily/question/july/ClassInitializedOrder$1
, can clearly see from the previous invokeDynamic
become a new anonymous class, then it's the result?
Still the block. We try to change a line of code?
public class ClassInitializedOrder {
private static boolean initialized = false;
static {
println("static 代码块执行。");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//println("Runnable 代码块执行。");
System.out.println("Runnable 代码块执行。");
//initialized = true;
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
We see that we just modify a line of code System.out.println("Runnable 代码块执行。");
, then the result?
The implementation of a successful return. why? Continue to view bytecode
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=4, locals=2, args_size=0
0: iconst_0
1: putstatic #9 // Field initialized:Z
4: ldc #14 // String static 代码块执行。
6: invokestatic #3 // Method println:(Ljava/lang/Object;)V
9: new #15 // class java/lang/Thread
12: dup
13: new #16 // class com/sxzhongf/daily/question/july/ClassInitializedOrder$1
16: dup
17: invokespecial #17 // Method com/sxzhongf/daily/question/july/ClassInitializedOrder$1."<init>":()V
20: invokespecial #18 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
23: astore_0
24: aload_0
25: invokevirtual #19 // Method java/lang/Thread.start:()V
28: aload_0
29: invokevirtual #20 // Method java/lang/Thread.join:()V
32: goto 40
35: astore_1
36: aload_1
37: invokevirtual #22 // Method java/lang/InterruptedException.printStackTrace:()V
40: return
Check #16
to see or new
an anonymous class, and one is the same, why we can succeed? The current that is not dependent on the anonymous code information class of the main class. There is no up and down dependent, then we will not wait another situation occurs, of course, will not block.
Then there friends will ask, why wait for each other it? And here we join
have the association, we look at its implementation code.
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
We can see that, first of all it is synchronized
keyword modified, it shows that it also can only be one thread to access, and then look down, we can find concrete realization join, in fact, wait()
be achieved, when the program sub-thread when the main thread to wait for the implementation class initialization is complete, but also rely on some elements of the object in the main thread. Then they will start to wait for the main thread initialization is complete, at this time, according to the order of execution classloader to load classes in #16
will begin to wait, then the main class can not be initialized, causing each other now waiting phase.
Result
- Anonymous initialization can not rely on built-in classes outside the class initialization
- lambda expression
invokeDynamic
as part of the main class bytecode, the main class wait to begin initialization completion
In short, the class initialization phase, the object built-in class (Anonymous / Lambda) and the main class initialization interdependence can not appear