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可见性和指令重排相关推荐

  1. Java并发:volatile内存可见性和指令重排

    volatile两大作用 1.保证内存可见性 2.防止指令重排 此外需注意volatile并不保证操作的原子性. (一)内存可见性 1 概念 JVM内存模型:主内存和线程独立的工作内存 Java内存模 ...

  2. JVM学习--(二)内存模型、可见性、指令重排序

    我们将根据JVM的内存模型探索java当中变量的可见性以及不同的java指令在并发时可能发生的指令重排序的情况. 内存模型 首先我们思考一下一个java线程要向另外一个线程进行通信,应该怎么做,我们再 ...

  3. Java内存模型、volatile、原子性、可见性、有序性、happens-before原则

    目录 1.硬件的效率与一致性: 缓存一致性(Cache Coherence) 2.Java内存模型 2.1主内存与工作内存 2.2内存间的交互 2.3 volatile型变量的特殊规则 2.3.1 保 ...

  4. volatile是怎么保证可见性和有序性的,为什么无法保证原子性

    文章目录 1. JMM内存模型 2. 并发编程的三大特性 3. Volatile的原理分析 在了解volatile之前,先认识一下JMM内存模型和并发编程的三大特性! 1. JMM内存模型 Java内 ...

  5. 原子变量、volatile、synchronized的可见性和原子性比较

    为什么80%的码农都做不了架构师?>>>    jdk5提供了java.util.concurrent包,这个包并行功能强大,工具齐全,其中就包括原子变量atomic 那么我们先说说 ...

  6. java 轻量级同步volatile关键字简介与可见性有序性与synchronized区别 多线程中篇(十二)...

    概念 JMM规范解决了线程安全的问题,主要三个方面:原子性.可见性.有序性,借助于synchronized关键字体现,可以有效地保障线程安全(前提是你正确运用) 之前说过,这三个特性并不一定需要全部同 ...

  7. java volatile线程可见_吃透Java并发:volatile是怎么保证可见性的

    前言 volatile关键字能够保证可见性和有序性,但是volatile为什么能够保证可见性和有序性?为什么volatile又不能保证原子性? 今天,我们从CPU多核缓存架构出发,结合MESI缓存一致 ...

  8. java volatile内存屏障_volatile 和 内存屏障

    接下来看看volatile是如何解决上面两个问题的: 被volatile修饰的变量在编译成字节码文件时会多个lock指令,该指令在执行过程中会生成相应的内存屏障,以此来解决可见性跟重排序的问题. 内存 ...

  9. R语言dplyr包数据列重排(reorder)实战:把特定数据列移动到第一列、把特定数据列移动到最后一列、数据列多列重排、按照字母顺序重排数据列、把数据列反序

    R语言dplyr包数据列重排(reorder)实战:把特定数据列移动到第一列.把特定数据列移动到最后一列.数据列多列重排.按照字母顺序重排数据列.把数据列反序 目录

最新文章

  1. 038_JDK的Iterable接口
  2. html插入javascript变量,javascript如何引用变量?
  3. 思维探索者:从问题到答案的思维过程 像侦探一样思考
  4. springCloud五大组件--Eureka
  5. 剑指Offer之寻找链表中环的入口问题
  6. 调查作业时,注意 【 调查深度 】 ,以及总结 【 中间成果物 】
  7. 重写Java中equals和hashcode方法的一般规则
  8. 使用 URL 读取网络资源
  9. Android Studio Design界面不显示控件的解决方法
  10. 计算机辅助审计笔记,审计笔记2.0 盘点
  11. Linux下Mysql5.5的Cmake安装以及sphinx结合
  12. Android中的封装流式布局FlowLayout
  13. python基础代码技巧_5行Python代码实现批量打水印技巧,值得收藏|python基础教程|python入门|python教程...
  14. 动漫人物手绘线稿图,非常适合初学者临摹
  15. 查询建立连接的IP地址
  16. 拒绝踩坑!源码编译 tensorflow 解决 cuda 不配套 万金油方法
  17. Centos Development Tools 安装
  18. 开源真香 离线识别率高 Python 人脸识别系统
  19. mac 之文件以及文件夹的快速处理与管理
  20. 西门子S7-300型PLC与西门子S7200smart型PLC的以太网通讯例程

热门文章

  1. 【CUDA学习】计时方法
  2. 别人推荐的一首好听的歌
  3. Android 系统服务列表
  4. Makefile.am、Makefile.in、Makefile、configure.ac关系(十二)
  5. Camera系统之ISP综述(一)
  6. C++ make_shared() shared_ptr()用法
  7. Android Binder 分析——匿名共享内存(好文)
  8. C++ 命名空间三种用法
  9. python基础系统性学习
  10. 代码实现WordPress 在文章内容的段落中插入广告google adsense