您好,登錄后才能下訂單哦!
/**
* ClassInitializedOrder for : Java Classload Order Test
*
* @author <a href="mailto:magicianisaac@gmail.com">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);
}
initialized = true
initialized = false
程序執行的時候,App Classloader 會首先加載ClassInitializedOrder.class
, 按照類的順序依次執行。
private static boolean initialized = false;
我們都知道,static
塊會在類加載的時候初始化,那么下一步會執行到Thread thread = new Thread(() -> initialized = true);
我們先來看一下當前行的字節碼:
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
分析#12
可以看到當前行的處理需要()
也就是改匿名類本身來處理,InvokeDynamic
指令的在當前的執行又依賴于當前所處的主類,主類并沒有執行結束,因此它需要等待主類執行結束,因此會在此停頓,如下:
繼續查看字節碼:
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
查看#16
,我們可以看到這里變成了new #16 // class com/sxzhongf/daily/question/july/ClassInitializedOrder$1
,可以明顯看到從之前的invokeDynamic
變成了 new 一個匿名類,那么它的結果呢?
依然還是block.我們來換一行代碼試試?
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();
}
}
我們看到我們只是修改了一行代碼System.out.println("Runnable 代碼塊執行。");
,那么結果呢?
執行成功的返回了。為什么?繼續查看字節碼
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
查看#16
,看到的還是new
了一個匿名類,和上一個是一樣的,為什么就可以成功呢?這個在于當前匿名類中沒有依賴主類的代碼信息。不存在上下依賴,那么就不會出現相互等待的情況發生,當然也就不會出現block。
那么就有朋友會問,為什么會相互等待呢?這里和我們join
就有關聯了,我們來看一下它的實現代碼。
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;
}
}
}
我們可以看到,首先它是synchronized
關鍵詞修飾的,那就說明它同時只能被一個線程訪問,再往下看,我們能發現,join的具體實現,其實就是wait()
來實現,當子線程中的程序再等待main線程的實現類初始化完成的時候,又依賴了主線程中的某些元素對象。那么就會開始等待主線程初始化完成,這個時候,根據classloader加載類的執行順序,在#16
就會開始等待,那么主類無法初始化完成,造成相互等待現相。
invokeDynamic
作為主類字節碼的一部分,需要等待主類初始化完成才能開始執行總之,在類的初始化階段,不能出現內置類(匿名/Lambda)和主類初始化中相互依賴的對象
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。