理解 t != (t = tail)

最近在阅读《Java高并发程序设计》一书时,在 ConcurrentLinkedQueue 类源码分析部分,看到了这样一句代码:t != (t = tail)。第一眼看见这句代码时,下意识的就将其理解为以下两个步骤:

错误理解:
1.赋值:t = tail;
2.比较 t != t。

相信不少人在初看这段代码时也会产生这样的反应,显然,如果是这种逻辑的话,那结果将永远是 false,这种低级错误应该是不会发生在官方的源码中的。关于t != (t = tail),如果单从语义上理解的话,可以将其理解为以下两个步骤:

1.比较 t 与 tail 是否相等,即首先判断语句 t != tail;
2.将 tail 赋值给 t,即执行 t = tail;

为验证次说法,这里编写了一段简单的代码,如下:

public class EqualityJudgment {

	public static void main(String[] args) {
		String tail = "";
		String t = "a";
		boolean check = (t != (t = tail));
		System.out.print("(t != (t = tail)) = " + check);
	}

}

// 执行结果为:(t != (t = tail)) = true

显然,从执行结果来看,是符合上述第二种语义的,为进一步验证这种说法,可以利用 javap 工具查看t != (t = tail)在字节码层面究竟是如何工作的。

打开命令行,进入上述代码编译之后生成的 class 文件所在的文件夹,输入 javap -v EqualityJudgment(javap 的使用方法可以输入 javap -help 查看,具体格式为 javap < options > < classes >…),这里截取了部分相关的字节码,如下所示:

代码对应的部分字节码
就上图出现的部分相关的字节码,在下面的表格中给出了说明:

指令含义
ldc将int 、 float 或 String 型常量值从常量池中推送至栈顶
astore_n栈顶引用型数值存入第 n 个本地变量(n = 1~4)
aload_n第 n 个引用类型本地变量推送至栈顶(n = 1~4)
dup复制栈顶数值并将复制值压入栈顶
if_acmpeq比较栈顶两 int 型数值的大小,当结果等于 0 时跳转(此处的 0 代表 false)
iconst_n将 int 型 n 推送至栈顶(n = -1~5,-1对应的指令为 iconst_m1)
goto无条件跳转

将字节码指令与上表相对应,下图给出了简略的分析:
字节码指令分析

从上图可以看到,t != (t = tail) 在字节码层面的执行顺序大致是这样的:

1.将 t 的值压入栈顶;
2.将 tail 的值压入栈顶;
3.复制 tail 的值并将其压入栈顶;
4.将栈顶的值(即tail的值“a”)弹出并赋值给本地变量中的 t(此时本地变量中 t = “a”,但堆栈中的 t = “” 依旧是原值);
5.弹出并比较栈顶两元素,由于堆栈中的 t = “” 为原值,所以比较结果应该为0,即不相等;
6.故最后t != (t = tail)的判断结果为 true。

可以看到,在字节码层面上,虽然赋值是先进行的,但由于此次赋值是给本地变量赋值,而堆栈中的元素 t 是在赋值操作前被压入的,其值依旧为原值,所以此处的比较其实是 t 的原值在和 tail 作比较,结果当然就是不相等啦。当然,从代码表面的语义上看,t != (t = tail)就像是做了这么一件事:首先比较 t != tail,然后赋值 t = tail。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值