这个表达式出现在ConcurrentLinkedQueue的源码中,就是这么简短的一个表达式,搞的我是一头雾水,一脸懵逼。总是产生一种错觉,就是它应该永远为“false”。(在我心里括号最先执行,给t赋值tail,在比较左边t和右边t。)
下面就来分析下 t != (t = tail)
为啥子可以为true.
模拟一下场景:
public class Test{
public static void main(String[] args) {
Object t = new Object();
Object tail = new Object();
boolean eqs = t != (t = tail);
System.out.println(eqs);
}
}
从JVM指令方面寻找答案:
(用了Maven),执行一下这个main方法,在target下找到编译的Test.class,idea右键 Open In Terminal,在底部Terminal中,输入:
javap -c -v Test.class
<!--重点选项-->
-version 版本信息,其实是当前javap所在jdk的版本信息,不是class在哪个jdk下生成的。
-v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息)
-l 输出行号和本地变量表
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息.
反解析出JVM指令代码如下:
# 这里咱们重点看main方法和局部变量表
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=4, args_size=1
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>":()V
7: astore_1
8: new #2 // class java/lang/Object
11: dup
12: invokespecial #1 // Method java/lang/Object."<init>":()V
15: astore_2
16: aload_1
17: aload_2
18: dup
19: astore_1
20: if_acmpeq 27
23: iconst_1
24: goto 28
27: iconst_0
28: istore_3
29: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
32: aload_1
33: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
36: return
...
LocalVariableTable:
Start Length Slot Name Signature
0 37 0 args [Ljava/lang/String;
8 29 1 t Ljava/lang/Object;
16 21 2 tail Ljava/lang/Object;
29 8 3 eqs Z
- 0~7:调用了new指令创建对象【并将地址入栈】,dup复制栈顶元素再次入栈,调用invokespecial指令【弹出栈顶元素】找到对应的init(构造方法)执行,astore_1弹出栈顶元素,放入LocalVariableTable的 slot 1的位置(给t赋值)。8~15类似不分析 此部分多数为自己理解,可能有误
- 15:aload_1加载 slot 1,的值入栈
- 16:aload_2加载 slot 2,的值 入栈
- 17:dup复制栈顶元素入栈,18:弹出栈顶元素,并存储在 slot 1 相当于t = tail。此时slot1 已经是新的值了,但栈内还是旧的值。
- 此刻栈内还剩元素,tail 、旧的t的值,20:if_acmpeq 弹出两个值比较,tail 和 旧的t的值进行不想等比较,因此为true。这个指令后面的 27是指 不正确跳到第27个指令。
- 上面说了结果为true,23:将常量1入栈
- 24:goto跳转 28:将栈顶元素赋值给 solt 3 eqs = 1(也就是true)
- 29:获取到PrintStream方法
- 32:加载solt 1 的t 元素(第四步t已经便成了tail了)
- invokevirtual调用方法,并弹出栈顶元素作为参数。
完毕!!!
综上可知,boolean eqs = t != (t = tail)
其实类似于将tail赋值给t(局部变量表中t已经是tail的值 了),再用旧的t(栈里面还是旧的,因为在赋值之前,值已经入栈了)和tail(栈中还剩的一个tail的值)比较,因此为true。
(感谢以上博客,提供参考)
个人对这块也是一知半解,哪里表述的有误,请一定指出,赐我一场教化。
网友评论