【Java猫说】Java多线程以内存可见性(下篇)

阅读本文约“3分钟”segmentfault


上一次咱们说到synchronized互斥代码的实现过程,若是有忘记或不清楚的能够去上篇看看。
【Java猫说】Java多线程以内存可见性(上篇)安全

今天咱们了解下重排序。多线程

其使代码书写的顺序与实现执行的顺序不一样,指令重排序是编译器或处理器为了提升程序性能而作的优化,能够分为
一、编译器优化的重排序(编译器优化)
二、指令级并行重排序(处理器优化)
三、内存系统的重排序(处理器优化)性能

而as-if-serial语义原则是指:不管如何重排序,程序执行的结果应该与代码顺序执行的结果一致(Java编译器、运行时和处理器都会保证Java在单线程下遵循as-if-serial语义)优化

int num1 = 1;
int num2 = 2;
int sum = num1 + num2;

对于执行的单线程而言,第一、2行的顺序能够重排,但第3行不能
由此,重排序并不会给单线程带来内存可见性问题线程

可是在多线程中程序交错执行时,重排序可能会形成内存可见性问题code

这里罗列了几个缘由,致使共享变量在线程间不可见的缘由:
一、线程的交叉执行(synchronized原子性)
二、重排序结合线程交叉执行(synchronized原子性)
三、共享变量更新后的值没有在工做内存与主内存间及时更新(synchronized可见性)对象

而对于另外一个对象volatile而言其实现了可见性,可是不能保证原子性(不能保证volatile变量符合操做是的原子性)排序

深刻来讲,是经过加入内存屏障和禁止重排序优化来实现的。
一、对volatile变量执行写操做时,会在写操做后加一条store屏障指令
二、对volatile变量执行读操做时,会在读操做前加入一条load屏障指令内存

由此咱们能够分为读写volatile变量的两种操做。

线程写volatile变量的过程:
一、改变线程工做内存中volatile变量副本的值
二、将改变后的副本的值从工做内存刷新到主内存

线程读volatile变量的过程:
一、从主内存中读取volatile变量的最新值到线程的工做内存中
二、从工做内存中读取volatile变量的副本

注意:volatile是不能保证原子性的

想要在多线程中安全的使用volatile变量,必须同时知足一下几个条件:
一、对变量的写入操做不依赖其当前值

- 不知足:number++、count = count * 5
- 知足:boolean变量、记录数据变化的变量等

二、该变量没有包含在具备其余变量的不变式中

- 不知足:不变式 low < up

最后咱们来比较下这两个对象吧

- volatile不须要加锁,比synchronized更轻量级,不会阻塞线程
- 从内存可见性角度讲,volatile读至关于加锁,volatile写至关于解锁
- synchronized即能保证可见性,又能保证原子性,而volatile只能保证可见性,没法保证原子性
- volatile没有synchronized使用的普遍

本文已转载我的技术公众号:UncleCatMySelf
欢迎留言讨论与点赞
上一篇推荐:【Java猫说】Java多线程以内存可见性(上篇)
下一篇推荐:【Java猫说】Java对象的行为

相关文章
相关标签/搜索