首页 未命名正文

linux编程_Java平安公布的明白

云返利网 未命名 2020-05-26 09:07:28 13 0

看《Java并发编程实战》遇到如下问题

Java并发编程实战 PDF+源码  http://www.linuxidc.com/Linux/2014-09/106561.htm

代码:

/**
* Created by yesiming on 16/11/11.
*/
public class Holder {
private int n;

public Holder(int n) {
this.n = n;
}

public void assertSanity() {
if(n != n) {
throw new AssertionError("This statment is false.");
}
}
}

疑问是:assertSanity() 方式中的判断 n != n
n它怎么就能不等于n呢,它们是同一个变量呀

解惑:设想一下场景
有2个线程 A,B
A做的操作:Holder holder = new Holder(42);
B做的操作:
if(holder != null) {
  holder.assertSantiy();
}
对于线程A的操作,jvm执行时的步骤:1.栈里天生holder引用,2.执行组织函数,在堆里天生Holder的内存空间,而且给n赋值为42,3.把holder指向堆里天生的内存空间
问题是:上面的1,2,3步骤不是根据1,2,3的顺序执行的,执行引擎对指令重排序后,可能会根据1,3,2的顺序执行,也可能是其余顺序
效果:这样就导致当holder指向了堆里的内存空间时(这时holder不是null了),然则组织函数执行尚未完成,n还没有被赋值为42。

对于线程B的操作,assertSanity()方式编译后的指令如下:
注重看绿色部门的指令,
第一步:1: getfield #2 // Field n:I
取得n的值
第二部:5: getfield #2 // Field n:I
取得n的值
也就是说,在执行对照指令if_icmpeq之前,要让对照的两个数都进栈,然后做对照
那么,既然要进栈2次,也就可以推导出,当线程B操作第一步getField时,n没有被线程A赋值,那么这个n是0,之后,线程A修改了n的值,第二次进栈时,n的值已经是修改后的42了
这样,就会导致栈顶2个slot中的数,一个0,一个是42,必然会导致8: if_icmpeq的效果为真

解决方案:
把n界说成final,而且在声明Holder时,使用valetile关键字
final能保证Holder在组织方式执行时,不会被执行引擎重排序,也就不会泛起holder指向了Holder组织发生的内存空间,然则组织方式没有执行完成的情形(n没有被赋值)
valetile保证了在外部程序中,线程A和线程B对Holder的更新状态是随时可见的

【关于云返利网】

云返利网是阿里云、腾讯云、华为云产品推广返利平台,在各个品牌云产品官网优惠活动之外,云返利网还提供返利。您可以无门槛获得阿里云、华为云、腾讯云所有产品返利,在官网下单后就可以领取,无论是自己用、公司用还是帮客户采购,您个人都可以获得返利。云返利网的目标是让返利更多、更快、更简单!详情咨询13121395187(微信同号)