volatile指令重排_volatile可见性和指令重排
volatile关键字的2个作用
1.线程的可见性
2.防止指令重排
什么是线程的可见性?
线程的可见性 就是一个线程对一个变量进行更改操作 其他线程获取会获得最新的值。
线程在执行的行 操作主线程的变量。会将变量的副本拷贝一份到线程的工作区域(避免每次到主线程读取 提高效率),在更改后的一段时间内写入主内存
如下示例代码:
public class Accounting implementsRunnable {boolean quit=false;int i=0;
@Overridepublic voidrun() {while (!quit){
i++;
}
System.out.println("线程退出");
}public static void main(String[] args) throwsInterruptedException {
Accounting accounting= newAccounting();
Thread a1= new Thread(accounting, "a1");
Thread a2= new Thread(newRunnable() {
@Overridepublic voidrun() {try{
Thread.sleep(2000);
System.out.println("开始通知线程结束");
accounting.setQuit(true);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
});
a2.start();
a1.start();
Thread.sleep(1000);
}public booleanisQuit() {returnquit;
}public void setQuit(booleanquit) {this.quit =quit;
}
}
这段代码的逻辑就是线程a1 执行循环操作 a2 2秒后设置quit为true任务结束 打印 "线程退出";
那么真的能够成功退出吗?我们看看 线程执行在内存中的操作图
打印:
开始通知线程结束
a2 线程首先将自己工作线程的quit改为ture ,然后一定时间之后去将主内存的quit改为true ,但是a1线程始终是操作的是自己的工作内存的副本 所以死循环
这个时候在quit加上volatile关键字
volatile boolean quit=false;
打印
开始通知线程结束
线程退出
加上volatile关键字后。当一个线程对变量进行修改会更新自己的工作内存里面的值,然后立即将改动的值刷新到主内存,同时线程2的工作内存的quit副本缓存失效 下次直接到主内存读取 所以能够正常执行
记录一个小插曲
System.out.println,sychronized,Thread.sleep Thread.sleep 影响可见性?
System.out.println
public class Accounting implementsRunnable {boolean quit=false;int i=0;
@Overridepublic voidrun() {while (!quit){
i++;
System.out.println(i);
}
System.out.println("线程退出");
}public static void main(String[] args) throwsInterruptedException {
Accounting accounting= newAccounting();
Thread a1= new Thread(accounting, "a1");
Thread a2= new Thread(newRunnable() {
@Overridepublic voidrun() {try{
Thread.sleep(2000);
System.out.println("开始通知线程结束");
accounting.setQuit(true);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
});
a2.start();
a1.start();
Thread.sleep(1000);
}public booleanisQuit() {returnquit;
}public void setQuit(booleanquit) {this.quit =quit;
}
会发现没有加上volatile一样可以成功退出 。那我们上面说的 线程的内存处理 不成立了吗?
查资料说 是因为jvm对锁的优化。因为如果我们在循环里面加上sychronize同步锁 会产生大量的锁竞争 所以jvm优化过后
synchronized (this){while (!quit){//.....
}
}
但是我们并没有在while里面加锁啊。我们看看打印的方法源码
public void println(intx) {synchronized (this) {
print(x);
newLine();
}
}
sleep方法并没有加锁,为什么能够保证可见性
sleep是阻塞线程并不释放锁,让出cpu调度。 让出cpu调度后下次执行会刷新工作内存
指令重排
指令重排指在编译的时候,在不单线程运行不影响结果的情况下进行指令优化
如:
public classContext {boolean isLoad=false;
Object configuration=null;public voidloadConfiguration(){
System.out.println("正在加载配置文件");
configuration= newObject();
isLoad=true;
}public voidinitContext(){
System.out.println("正在进行初始化");
}public static voidmain(String[] args) {
Context context=newContext();
context.loadConfiguration();if(context.isLoad){
context.initContext();
}
}
}
这段代码就是先加载配置文件信息 然后初始化上下文
我们在单线程下 把他们的顺序调换模拟指令重排 会对结果没有影响
public voidloadConfiguration(){
isLoad=true;
System.out.println("正在加载配置文件");
configuration= newObject();
}
但是在多线程下面
public classContext {boolean isLoad=false;
Object configuration=null;public voidloadConfiguration(){//模拟jvm指令重排 将isLoad命令排在第一位
isLoad=true;/***
* 模拟并发情况下指令重排。导致的isload=true排到前面。
* 这个时候配置文件没初始化。initContext监听到lsLoad等于true根据配置文件进行初始化*/
try{
Thread.sleep(2000);
}catch(InterruptedException e) {
e.printStackTrace();
}
configuration= newObject();//isLoad=true;指令重排前
}public voidinitContext(){
configuration.toString();
System.out.println("正在进行初始化");
}public static voidmain(String[] args) {
Context context=newContext();//负责监听 如果加载完毕 则进行上下午初始化
Thread t2=new Thread(newRunnable() {
@Overridepublic voidrun() {while (true){if(context.isLoad){
context.initContext();break;
}
}
}
},"t2");//负责加载配置文件
Thread t1=new Thread(newRunnable() {
@Overridepublic voidrun() {
context.loadConfiguration();
}
},"t1");
t1.start();
t2.start();
}
}
只是模拟指令重排 先不考虑可见性 这种情况会初始化context 没有configuration 报错 使用volatile关键字修饰可以避免
值得注意的一点
volatile虽然能够保证线程的可见性 但是并不能保证原子性 比如i++操作 都是读出i的值 进行运算再写入。如果在读出的时候别的线程改变了 就会不一致
哪种场景适合用volatile 对一个变量的值进行修改 不依赖其他值。 比如 index=true 而不是i=i+j;或则index=j>a 或 a=j (会从内存中读出j的值 然后赋值到a);
java提供atomic cas能够性能比锁高能够保证原子性 如:atomicInt atomictDouble
volatile指令重排_volatile可见性和指令重排相关推荐
- Java并发:volatile内存可见性和指令重排
volatile两大作用 1.保证内存可见性 2.防止指令重排 此外需注意volatile并不保证操作的原子性. (一)内存可见性 1 概念 JVM内存模型:主内存和线程独立的工作内存 Java内存模 ...
- JVM学习--(二)内存模型、可见性、指令重排序
我们将根据JVM的内存模型探索java当中变量的可见性以及不同的java指令在并发时可能发生的指令重排序的情况. 内存模型 首先我们思考一下一个java线程要向另外一个线程进行通信,应该怎么做,我们再 ...
- Java内存模型、volatile、原子性、可见性、有序性、happens-before原则
目录 1.硬件的效率与一致性: 缓存一致性(Cache Coherence) 2.Java内存模型 2.1主内存与工作内存 2.2内存间的交互 2.3 volatile型变量的特殊规则 2.3.1 保 ...
- volatile是怎么保证可见性和有序性的,为什么无法保证原子性
文章目录 1. JMM内存模型 2. 并发编程的三大特性 3. Volatile的原理分析 在了解volatile之前,先认识一下JMM内存模型和并发编程的三大特性! 1. JMM内存模型 Java内 ...
- 原子变量、volatile、synchronized的可见性和原子性比较
为什么80%的码农都做不了架构师?>>> jdk5提供了java.util.concurrent包,这个包并行功能强大,工具齐全,其中就包括原子变量atomic 那么我们先说说 ...
- java 轻量级同步volatile关键字简介与可见性有序性与synchronized区别 多线程中篇(十二)...
概念 JMM规范解决了线程安全的问题,主要三个方面:原子性.可见性.有序性,借助于synchronized关键字体现,可以有效地保障线程安全(前提是你正确运用) 之前说过,这三个特性并不一定需要全部同 ...
- java volatile线程可见_吃透Java并发:volatile是怎么保证可见性的
前言 volatile关键字能够保证可见性和有序性,但是volatile为什么能够保证可见性和有序性?为什么volatile又不能保证原子性? 今天,我们从CPU多核缓存架构出发,结合MESI缓存一致 ...
- java volatile内存屏障_volatile 和 内存屏障
接下来看看volatile是如何解决上面两个问题的: 被volatile修饰的变量在编译成字节码文件时会多个lock指令,该指令在执行过程中会生成相应的内存屏障,以此来解决可见性跟重排序的问题. 内存 ...
- R语言dplyr包数据列重排(reorder)实战:把特定数据列移动到第一列、把特定数据列移动到最后一列、数据列多列重排、按照字母顺序重排数据列、把数据列反序
R语言dplyr包数据列重排(reorder)实战:把特定数据列移动到第一列.把特定数据列移动到最后一列.数据列多列重排.按照字母顺序重排数据列.把数据列反序 目录
最新文章
- 038_JDK的Iterable接口
- html插入javascript变量,javascript如何引用变量?
- 思维探索者:从问题到答案的思维过程 像侦探一样思考
- springCloud五大组件--Eureka
- 剑指Offer之寻找链表中环的入口问题
- 调查作业时,注意 【 调查深度 】 ,以及总结 【 中间成果物 】
- 重写Java中equals和hashcode方法的一般规则
- 使用 URL 读取网络资源
- Android Studio Design界面不显示控件的解决方法
- 计算机辅助审计笔记,审计笔记2.0 盘点
- Linux下Mysql5.5的Cmake安装以及sphinx结合
- Android中的封装流式布局FlowLayout
- python基础代码技巧_5行Python代码实现批量打水印技巧,值得收藏|python基础教程|python入门|python教程...
- 动漫人物手绘线稿图,非常适合初学者临摹
- 查询建立连接的IP地址
- 拒绝踩坑!源码编译 tensorflow 解决 cuda 不配套 万金油方法
- Centos Development Tools 安装
- 开源真香 离线识别率高 Python 人脸识别系统
- mac 之文件以及文件夹的快速处理与管理
- 西门子S7-300型PLC与西门子S7200smart型PLC的以太网通讯例程
热门文章
- 【CUDA学习】计时方法
- 别人推荐的一首好听的歌
- Android 系统服务列表
- Makefile.am、Makefile.in、Makefile、configure.ac关系(十二)
- Camera系统之ISP综述(一)
- C++ make_shared() shared_ptr()用法
- Android Binder 分析——匿名共享内存(好文)
- C++ 命名空间三种用法
- python基础系统性学习
- 代码实现WordPress 在文章内容的段落中插入广告google adsense