问题描述 案例代码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 <ignore_js_op> 案例2 <ignore_js_op>
可以看到除了多出两个safepoint之外没有其他不同,并且safepoint类型不同,具体原因还是没能找到答案,只能查看对应的汇编代码含义再继续分析原因 |
|
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架