Java安全发布的理解
看《Java并发编程实战》遇到如下问题
代码:
/**
* 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()方法编译后的指令如下:
public void assertSanity();
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field n:I
4: aload_0
5: getfield #2 // Field n:I
8: if_icmpeq 21
11: new #3 // class java/lang/AssertionError
14: dup
15: ldc #4 // String This statment is false.
17: invokespecial #5 // Method java/lang/AssertionError."<init>":(Ljava/lang/Object;)V
20: athrow
21: return
LineNumberTable:
line 14: 0
line 15: 11
line 17: 21
LocalVariableTable:
Start Length Slot Name Signature
0 22 0 this Lo1/Holder;
}
注意看绿色部分的指令,
第一步: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的更新状态是随时可见的
Java安全发布的理解的更多相关文章
- Java语言的个人理解
Java语言的个人理解(比价深层次吧) 大四的生活确实十分的奢靡,不锻炼,不读书,几乎就是当一天和尚撞一天钟的生活,太颓废了,还好自己不是这个样子,不过身体确实差了很多,昨天跑了一圈内环(4KM),今 ...
- Java常量字符串String理解
Java常量字符串String理解 以前关于String的理解仅限于三点:1.String 是final类,不可继承2.String 类比较字符串相等时时不能用“ == ”,只能用 "eq ...
- java的final变量理解
java的final变量理解 final基本类型是数值不能改变 final对象是引用不能改变,对象其自身是可以被修改的.
- 谈谈我对Java中CallBack的理解
谈谈我对Java中CallBack的理解 http://www.cnblogs.com/codingmyworld/archive/2011/07/22/2113514.html CallBack是回 ...
- Gradle 1.12用户指南翻译——第46章. Java 库发布插件
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- java项目发布到linux服务器,tomcat正常启动但没加载项目
问题描述: java项目发布到linux服务器,一切操作正确,linux命令启动tomcat后,查看日志启动tomcat正常,上传的war包已经解压成功,但是tomcat没加载项目. 解决方法: 1. ...
- 【Java】「深入理解Java虚拟机」学习笔记(1) - Java语言发展趋势
0.前言 从这篇随笔开始记录Java虚拟机的内容,以前只是对Java的应用,聚焦的是业务,了解的只是语言层面,现在想深入学习一下. 对JVM的学习肯定不是看一遍书就能掌握的,在今后的学习和实践中如果有 ...
- Java提高篇之理解java的三大特性——继承
在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...
- 【转】java提高篇(二)-----理解java的三大特性之继承
[转]java提高篇(二)-----理解java的三大特性之继承 原文地址:http://www.cnblogs.com/chenssy/p/3354884.html 在<Think in ja ...
随机推荐
- 程序员面试宝典题目重温-P1-100
int f(int x ,int y){ return (x&y) + ((x^y)>>1)} f(729,271)输出是什么? x&y表示按位与,结果是x,y相同位 ...
- Replicate String in C#
My original posting on string repetition caused a couple responses, and is currently among the Top P ...
- Linux 配置多IP
这里以红帽Linux为例.假定原系统已配置一个IP,地址为:192.168.20.140,配置文件路径/etc/sysconfig/network-script/ifcfg-eth0.现在需要配置一个 ...
- Javascript面向对象编程(三):非构造函数的继承 by 阮一峰
今天是最后一个部分,介绍不使用构造函数实现"继承". 一.什么是"非构造函数"的继承? 比如,现在有一个对象,叫做"中国人". var Ch ...
- 【转】中兴G718C卡刷刷机教程(青漾2 4G)--不错
原文网址:http://www.zterom.com/guide/2278.html 刷机包 B11纯净版 适合长久使用_B11_lite_0130.zip 刷机用了20多分钟. 在和大家分享过中兴G ...
- FZYZOJ-1880 【UFO】水管
P1880 -- [ufo]水管 时间限制:1000MS 内存限制:131072KB 通过/提交人数:32/100 状态: 标签: 数学问题-组合数学 无 ...
- Nodejs in Visual Studio Code 12.构建单页应用Scrat实践
1.开始 随着前端工程化深入研究,前端工程师现在碉堡了,甚至搞了个自己的前端网站http://div.io/需要邀请码才能注册,不过里面的技术确实牛.距离顶级的前端架构,目前博主应该是far away ...
- Ubuntu中Nginx的安装与配置
原文地址:http://www.cnblogs.com/languoliang/archive/2013/04/01/nginx.html 1.Nginx介绍 Nginx是一个非常轻量级的HTTP服务 ...
- springboot的restController使用swagger遇到的问题。
在controller中使用swagger,使用注解ApiImplicitParam遇到一个问题 当方法的参数是走path的swggerui的参数展现是正常的, @PathVariable 但如果是走 ...
- AngularJS测试二 jasmine测试路由 控制器 过滤器 事件 服务
测试应用 1.测试路由 我们需要检测路由是否在运作,是否找到了,或者是404了.我们要确认路由事件触发了,预期的模板是否真的加载了.既然路由会改变页面的地址(URL)和页面内容,我们需要检测路由是否被 ...