【java】Java 原子性、有序性与Happens-Before
文章目录
- 1.概述
- 2.产生并发Bug的源头
- 3.线程切换带来的原子性问题
- 三、编译优化带来的有序性问题#
- 四、Java内存模型#
- 五、Happens-Before规则
- 5.1 规则1:程序的顺序性规则
- 5.2 规则2:volatile变量规则#
- 5.3 规则3:传递性#
- 5.4 规则4:管程中锁的规则
- 5.5 规则5:线程start()规则
- 5.6 规则6:线程join()规则
- 六、final
1.概述
转载:Java核心复习—— 原子性、有序性与Happens-Before
视频:
2.产生并发Bug的源头
可见性
缓存导致的可见性问题原子性
线程切换带来的原子性问题有序性
编译优化带来的有序性问题
上面讲到了 volatile 与可见性,本章再主要讲下原子性、有序性与Happens-Before规则。
3.线程切换带来的原子性问题
count += 1 这一句高级语言的语句,往往需要多条CPU执令。可以分为3步:
- 将count值加载到寄存器
- 在寄存器中对count进行+1操作
- 将count值写回内存
所以,我们需要在高级语言的层面上,确保一些操作是原子性操作。
三、编译优化带来的有序性问题#
编译器为了优化性能,有时会改变程序中语句的先后顺序。
a = 6;
b = 7;
经过编译优化后,可能会变成
b = 7;
a = 6
双重检查创建单例对象
public class Singleton{static Singleton instance;static Singleton getInstance(){if(instance == null){synchronized(Singleton.class){if(instance == null){instance = new Singleton();}}}}}
这个例子看似很完美,但其实是可能触发空指针异常。
为什么可能会触发空指针异常。
假设getInstance()的运行过程是这样:
- 开辟一块M内存空间
- 在M内存空间上创建Singleton对象
- 将对象赋值给instance
这样的话是没问题的。但编译时并非按这个顺序来的,而是按照下面的顺序来:
- 开辟一块M内存空间
- 将M内存空间的地址赋值给instance
- 在M内存空间创建Singleton对象。
当A线程走到了第2步,将M内空空间的地址赋值给instance时,发生线程切换,则B线程判断instance == null时,结果返回false,则返回null,导致返回空指针。
四、Java内存模型#
上面说到产生并发Bug的源头是缓存导致的可见性、编译优化导致的顺序性。如果禁用缓存和编译优化是不是就问题解决了,并不是,将引入最大的问题,程序性能问题。
合理的方案是按需求来禁用缓存和编译优化。
Java内存模型规范了按需禁用缓存和编译优化的方法(volatile、synchronized、final、Happens-Before规则)。
class VolatileExample{int x = 0;volatile boolean v = false;public void writer(){x = 42;v = true;}publci void reader(){if(v == true){//x = ?sout(x);}}}
上面的例子,A线程调用writer(),B线程调用reader(),B看到的x是多少?JDK1.5以前是0,JDK1.5及以上是42。
原因是JDK1.5对volatile进行增强,新增了Happens-Before规则。
五、Happens-Before规则
5.1 规则1:程序的顺序性规则
意思就是:前面一个操作的结果对后续操作是可见的。
x = 42 ; Hapens-Before于 v = true;
如果是在JDK1.5以前,v = true可能会先被执行。
class VolatileExample{int x = 0;volatile boolean v = false;public void writer(){x = 42;v = true;}publci void reader(){if(v == true){//x = ?sout(x);}}}
5.2 规则2:volatile变量规则#
对一个volatile变量的写操作,Happens-Before于后续对这个volatile变量的读操作。
5.3 规则3:传递性#
A Happens-Before B
B Happens-Before C
那么
A Happens-Before C
class VolatileExample{int x = 0;volatile boolean v = false;public void writer(){x = 42;v = true;}publci void reader(){if(v == true){//x = ?sout(x);}}}
规则2结合规则3和规则1来一起看。
x = 42 Happens-Before v = true
,这是规则1
A线程写变量v=true Happens-Before B线程读变量v=true
,这是规则2
由规则1、2结合规则3的传递性,得出x = 42 Happens-Before B线程读变量v=true
5.4 规则4:管程中锁的规则
对一个锁的解锁 Happens-Before于后续对这个锁的加锁。
管程是一种通用的同步原语。同步原语是什么。。。。synchronized是Java对管程的实现。
synchronized(this){//此处自动加锁// x 是共享变量,初始值=10if(this.x < 12){this.x = 12;}}//此处自动解锁
A执行完代码块后x=12,执行完释放锁。线程B进入代码块,能够看到A对x的写操作。
5.5 规则5:线程start()规则
主线程A启动子线程B后,子线程B能够看到主线程在启动子线程B之前的操作。
也就是 start()操作 Happens-Before 线程B中的任意操作。
Thread B = new Thread(() -> {//主线程调用B.start()之前//所有对共享变量的修改,此处可见//var == 77
})var = 77;
B.start();
5.6 规则6:线程join()规则
这条是关于线程等待的。主线程A等待子线程B完成(A调用子线程B的join()方法)。当子线程B完成后,主线程能够看到子线程的操作。
Thread B = new Thread(()-> {var = 66;})//一系列操作B.start();//进行一系列操作B.join()
线程B中的任意操作,Happens-Before 于该join()操作。
六、final
final修饰变量时,初衷是告诉编译器:这个变量生而不变,可以尽可能的优化。
那什么时候使用final呢?
一个答案就是“尽可能的使用”。任何你不希望改变的(基本类型,或者指向一个对象,不管该对象是否可变)一般来讲都应该声明为final。
另一种看待此问题的方式是:
如果一个对象将会在多个线程中访问并且你并没有将其成员声明为final,则必须提供其他方式保证线程安全
【java】Java 原子性、有序性与Happens-Before相关推荐
- java 并发原子性与易变性 来自thinking in java4 21.3.3
java 并发原子性与易变性 具体介绍请參阅thinking in java4 21.3.3 thinking in java 4免费下载:http://download.csdn.net/deta ...
- [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[JDBC](1)
MIS信息管理系统实战开发之使用MySQL实现保存 开发背景 ID.姓名.年龄为公共信息,而学生有成绩,工人有工资 定义一个抽象类Person(ID.姓名.年龄),学生是其子类,有成绩,工人是其子类有 ...
- 零基础学JAVA]Java SE基础部分-01. Java发展及JDK配置
1.课程名称:Java发展及JDK配置 本季介绍了JAVA的发展过程,包括JDK的发展历程,path路径的配置和classpath的配置及作用.并简单讲解了一个简单的JAVA程序,并通过此程序讲解了J ...
- Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList)
文章目录 Pre 概述 fail-safe的容器-CopyOnWriteArrayList add remove函数 例子 缺陷 使用场景 Pre Java - Java集合中的快速失败Fail Fa ...
- Java - Java集合中的快速失败Fail Fast 机制
文章目录 什么是 fail-fast 源码解读 Itr 为什么对集合的结构进行修改会发生并发修改异常-源码分析 修改方法之 remove 修改方法之 add 案例分享 [案例一] [案例二] [案例三 ...
- java比python难_python java JAVA比python难多少?
python和java哪个好? 学python好还是java好一次一次的失望后来渐渐放弃,哪怕后来有人提起你的名字小编脸上也不会泛起红晕. Python Python是一种面向对象的解释型计算机程序设 ...
- [Jmeter]通过批处理调用java,java从CSV动态读取登录的用户名和密码,并将其作为参数组合成字符串,写入外部.bat文件,然后通过Java执行这个外部批处理文件...
问题1:怎样通过批处理调用java代码? 问题2:怎样通过java从CSV文件获取到用户名和密码存入变量? 问题3:怎样将获取到的用户名和密码组合成字符串,写入外部批处理文件? 问题4:怎样在批处理文 ...
- java java se_Java 8 SE可选,严格的方法
java java se 大约两周前,Stephen Colebourne提出了使用Optional的实用方法 . 如果您阅读了它,您可能会从我以前的建议中猜到我不同意. 总览 我必须以免责声明开头, ...
- java java编码_Java编码约定被认为是有害的
java java编码 在Oracle网站上有Java编程语言的官方代码约定指南. 您可能希望这份超过20页的文档将是有关Java语言的最佳实践,提示和技巧的最完整,最全面和最权威的来源. 但是一旦您 ...
- java java se_Java SE 11:推动Java前进
java java se 介绍 在我看来,这篇文章提出了Java语言应该如何发展成为一种首选语言. 它还提供了一些我喜欢但是有时(可能永远不会)成为Java的一部分的功能,出于某些原因,我将对此加以解 ...
最新文章
- 使用js冒泡实现点击空白处关闭弹窗
- 博客园添加访问人数统计超详细教程
- gradlew wrapper使用下载到本地的gradle.zip文件装配--转
- [JOYOI] 1124 花店橱窗
- docker化你的java应用(上)
- Linux的Configure详情
- 提升领导力 六商是基础
- javascript四个方向无间隙滚动合集(多浏览器IE,firefox兼容)
- MySQL服务器硬件和操作系统调节
- php pdo 关闭,php pdo预处理
- [华为机试练习题]37.合唱队
- 模型人的因素_肺癌风险预测模型中纳入肺功能如何?
- 网络设备中的路由器的作用,如何设置路由器,上网、IP分配、黑白名单、访问量...
- 基于Simulink与FlightGear联合建模并仿真多旋翼无人机在平衡态的动态控制
- linux系统 安装主板驱动,I810 Graphics LINUX Driver的安装
- 形容计算机技术发展的词,形容技术发展的成语是什么_四字词语 - 成梦词典
- 判断闰年的3种方法(判断+范围输出)
- 开发可以自动运行程序的U盘
- 风影ASP.NET基础教学 13 NET3.0特性补充
- 判断字符串中是否仅包含英文字母、数字和汉字
热门文章
- 让每一首心动歌曲穿越人海遇见你,背后竟藏着这么多“黑科技”|回响·TME音乐公开课...
- 谁给小鹏P5的勇气?
- 4月份全球新注册39.2万辆电动汽车 榜首并非Model 3
- 2020魔幻“商战”:抢公章、发黄图、半夜翻工厂...最烂的小说都不敢这么写
- 王国权辞任中国电信股份有限公司执行副总裁
- 理想更新“货车并线预警”遭用户吐槽 李想:仍在优化
- iPhone 12发布前夜:富士康奖励万元招不到人,有工人国庆连轴转
- 能自定义桌面后,iOS用户玩疯了
- iOS版微信跟上了!已支持改微信号,修改后好友不会收到提醒
- 王自如、罗永浩将一起出镜直播带货?罗永浩亲自回应