美文网首页
t != (t = tail) 可以为 true ?!

t != (t = tail) 可以为 true ?!

作者: ________方块丶 | 来源:发表于2020-02-27 01:32 被阅读0次

这个表达式出现在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。

(感谢以上博客,提供参考)

个人对这块也是一知半解,哪里表述的有误,请一定指出,赐我一场教化。

相关文章

网友评论

      本文标题:t != (t = tail) 可以为 true ?!

      本文链接:https://www.haomeiwen.com/subject/euqachtx.html