/** * 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
, 按照類的順序依次執行。java
private static boolean initialized = false;
bash
咱們都知道,static
塊會在類加載的時候初始化,那麼下一步會執行到Thread thread = new Thread(() -> initialized = true);
咱們先來看一下當前行的字節碼:ide
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
指令的在當前的執行又依賴於當前所處的主類,主類並無執行結束,所以它須要等待主類執行結束,所以會在此停頓,以下:函數
繼續查看字節碼:spa
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.咱們來換一行代碼試試?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(); } }
咱們看到咱們只是修改了一行代碼System.out.println("Runnable 代碼塊執行。");
,那麼結果呢?對象
執行成功的返回了。爲何?繼續查看字節碼ip
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。ssl
那麼就有朋友會問,爲何會相互等待呢?這裏和咱們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)和主類初始化中相互依賴的對象