问题描述
案例代码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类型不同,具体原因还是没能找到答案,只能查看对应的汇编代码含义再继续分析原因
|
|