/** * 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)和主类初始化中相互依赖的对象