问题描述 案例代码1中属性没有volatile修饰,主线程修改其值,线程中是看不到其变更的,所以会一直死循环 案例代码2中属性同样没有volatile修饰,但是主线程修改其值,线程中看到了其变更的最新值,线程正常退出。为什么? 案例1:会死循环 package org.gallant.jitwatch; /** * -server -XX:+UnlockDiagnosticVMOptions -XX:+TraceClassLoading -XX:+PrintAssembly -XX:+LogCompilation -XX:+DebugNonSafepoints -XX:LogFile=VisibilityWithoutVolatile.log -Xcomp -XX:CompileCommand=compileonly,*VisibilityWithoutVolatile.* -XX:CompileCommand=dontinline,*VisibilityWithoutVolatile.* * @author 会灰翔的灰机 * @date 2019/10/30 */ public class VisibilityWithoutVolatile extends Thread { private boolean isRun = true; @Override public void run() { while(isRun){ } } public static void main(String[] args) throws InterruptedException { VisibilityWithoutVolatile visibility = new VisibilityWithoutVolatile(); visibility.start(); Thread.sleep(1000); visibility.isRun = false; System.out.println("stop thread"); } } 案例2:不会死循环 package org.gallant.jitwatch; /** * -server -XX:+UnlockDiagnosticVMOptions -XX:+TraceClassLoading -XX:+PrintAssembly -XX:+LogCompilation -XX:+DebugNonSafepoints -XX:LogFile=VisibilityWithoutVolatileHavePrint.log -Xcomp -XX:CompileCommand=compileonly,*VisibilityWithoutVolatileHavePrint.* -XX:CompileCommand=dontinline,*VisibilityWithoutVolatileHavePrint.* * @author 会灰翔的灰机 * @date 2019/10/30 */ public class VisibilityWithoutVolatileHavePrint extends Thread { private boolean isRun = true; @Override public void run() { while(isRun){ System.out.println(isRun); } } public static void main(String[] args) throws InterruptedException { VisibilityWithoutVolatileHavePrint visibility = new VisibilityWithoutVolatileHavePrint(); visibility.start(); Thread.sleep(1000); visibility.isRun = false; System.out.println("stop thread"); } } 问题分析 使用javap分析底层字节码 案例1 public class org.gallant.jitwatch.VisibilityWithoutVolatile extends java.lang.Thread ... { public org.gallant.jitwatch.VisibilityWithoutVolatile(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Thread."<init>":()V 4: aload_0 5: iconst_1 6: putfield #2 // Field isRun:Z 9: return LineNumberTable: line 8: 0 line 9: 4 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lorg/gallant/jitwatch/VisibilityWithoutVolatile; public void run(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #2 // Field isRun:Z 4: ifeq 10 7: goto 0 10: return LineNumberTable: line 13: 0 line 15: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 this Lorg/gallant/jitwatch/VisibilityWithoutVolatile; StackMapTable: number_of_entries = 2 frame_type = 0 /* same */ frame_type = 9 /* same */ public static void main(java.lang.String[]) throws java.lang.InterruptedException; descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: new #3 // class org/gallant/jitwatch/VisibilityWithoutVolatile 3: dup 4: invokespecial #4 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #5 // Method start:()V 12: ldc2_w #6 // long 1000l 15: invokestatic #8 // Method java/lang/Thread.sleep:(J)V 18: aload_1 19: iconst_0 20: putfield #2 // Field isRun:Z 23: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream; 26: ldc #10 // String stop thread 28: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 31: return LineNumberTable: line 17: 0 line 18: 8 line 19: 12 line 20: 18 line 21: 23 line 22: 31 LocalVariableTable: Start Length Slot Name Signature 0 32 0 args [Ljava/lang/String; 8 24 1 visibility Lorg/gallant/jitwatch/VisibilityWithoutVolatile; Exceptions: throws java.lang.InterruptedException } 案例2 public class org.gallant.jitwatch.VisibilityWithoutVolatileHavePrint extends java.lang.Thread ... { public org.gallant.jitwatch.VisibilityWithoutVolatileHavePrint(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Thread."<init>":()V 4: aload_0 5: iconst_1 6: putfield #2 // Field isRun:Z 9: return LineNumberTable: line 8: 0 line 9: 4 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lorg/gallant/jitwatch/VisibilityWithoutVolatileHavePrint; public void run(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: getfield #2 // Field isRun:Z 4: ifeq 20 7: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 10: aload_0 11: getfield #2 // Field isRun:Z 14: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V 17: goto 0 20: return LineNumberTable: line 13: 0 line 14: 7 line 16: 20 LocalVariableTable: Start Length Slot Name Signature 0 21 0 this Lorg/gallant/jitwatch/VisibilityWithoutVolatileHavePrint; StackMapTable: number_of_entries = 2 frame_type = 0 /* same */ frame_type = 19 /* same */ public static void main(java.lang.String[]) throws java.lang.InterruptedException; descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: new #5 // class org/gallant/jitwatch/VisibilityWithoutVolatileHavePrint 3: dup 4: invokespecial #6 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #7 // Method start:()V 12: ldc2_w #8 // long 1000l 15: invokestatic #10 // Method java/lang/Thread.sleep:(J)V 18: aload_1 19: iconst_0 20: putfield #2 // Field isRun:Z 23: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 26: ldc #11 // String stop thread 28: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 31: return LineNumberTable: line 18: 0 line 19: 8 line 20: 12 line 21: 18 line 22: 23 line 23: 31 LocalVariableTable: Start Length Slot Name Signature 0 32 0 args [Ljava/lang/String; 8 24 1 visibility Lorg/gallant/jitwatch/VisibilityWithoutVolatileHavePrint; Exceptions: throws java.lang.InterruptedException } 除了多出几行指令外没有什么进展。。。 使用hsdis与jitwatch分析机器指令 案例1 案例2 可以看到除了多出两个safepoint之外没有其他不同,并且safepoint类型不同,具体原因还是没能找到答案,只能查看对应的汇编代码含义再继续分析原因