对于内存模型的三大特性:有序性、原子性、可见性。

大家都知道 volatile 能保证可见性和有序性但是不能保证原子性,但是为什么呢?

一、原子性、有序性、可见性

1、原子性:

(1)原子的意思代表着 ——“不可分”;
(2)在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。原子性是拒绝多线程交叉操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。例如 a=1 是原子性操作,但是 a++ 和 a += 1 就不是原子性操作。

2、可见性

线程执行结果在内存中对其它线程的可见性。

变量经过 volatile 修饰后,对此变量进行写操作时,汇编指令中会有一个 LOCK 前缀指令,加了这个指令后,会引发两件事情:

发生修改后强制将当前处理器缓存行的数据写回到系统内存。
这个写回内存的操作会使得在其他处理器缓存了该内存地址无效,重新从内存中读取。
3、有序性

在本线程内观察,所有操作都是有序的(即指令重排不会导致单线程程序执行结果与排序前有任何差别)。在一个线程观察另一个线程,所有操作都是无序的,无序是因为发生了指令重排序。在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

二、线程安全的两个问题,执行控制和内存可见

执行控制(synchronize):控制代码只能顺序执行(执行一次只能被一个线程执行)或者可以多线程并发执行。

内存可见控制(volatile):线程执行结果在内存中对其它线程的可见性。线程在具体执行时,会先拷贝主存数据到线程本地(CPU 缓存),操作完成后再把结果从线程本地刷到主存。

volatile 和 synchronize 两个关键字就是上述两种作用。

synchronize 关键字使得同一时刻只有一个线程可以获得当前变量、方法、类的锁,其他线程无法访问,也就无法同步并发执行,synchronized 还会创建一个内存屏障,内存屏障指令保证了所有 CPU 操作结果都会直接刷到主存中,从而保证了操作的内存可见性,同时也使得先获得这个锁的线程的所有操作,都 happens-before 于随后获得这个锁的线程的操作,保障有序性、可见性、原子性;
volatile 通过强制将当前线程修改后的值写回内存并使得其他线程中该值无效的方式保证其可见性,通过禁止指令重排的方式保证有序性,具体为何不能保证原子性在下一部分讨论。

三、为什么 volatile 不能保证原子性

对于 i=1 这个赋值操作,由于其本身是原子操作,因此在多线程程序中不会出现不一致问题,但是对于 i++ 这种复合操作,即使使用 volatile 关键字修饰也不能保证操作的原子性,可能会引发数据不一致问题。

private volatile int i = 0;
i++;
如果启了 500 条线程并发地去执行 i++ 这个操作 最后的结果 i 是小于 500 的

复制代码
i++ 操作可以被拆分为三步:

  1,线程读取 i 的值2、i 进行自增计算3、刷新回 i 的值

复制代码
网上一些博客的解释是:

假设某一时刻 i=5,此时有两个线程同时从主存中读取了 i 的值,那么此时两个线程保存的 i 的值都是 5, 此时 A 线程对 i 进行了自增计算,然后 B 也对 i 进行自增计算,此时两条线程最后刷新回主存的 i 的值都是 6(本来两条线程计算完应当是 7)所以说 volatile 保证不了原子性。

我的不解之处在于:

既然 i 是被 volatile 修饰的变量,那么对于 i 的操作应该是线程之间是可见的啊,就算 A.,B 两个线程都同时读到 i 的值是 5,但是如果 A 线程执行完 i 的操作以后应该会把 B 线程读到的 i 的值置为无效并强制 B 重新读入 i 的新值也就是 6 然后才会进行自增操作才对啊。

后来参照其他博客终于想通了:

1、线程读取 i

2、temp = i + 1

3、i = temp
当 i=5 的时候 A,B 两个线程同时读入了 i 的值, 然后 A 线程执行了 temp = i + 1 的操作, 要注意,此时的 i 的值还没有变化,然后 B 线程也执行了 temp = i + 1 的操作,注意,此时 A,B 两个线程保存的 i 的值都是 5,temp 的值都是 6, 然后 A 线程执行了 i = temp (6)的操作,此时 i 的值会立即刷新到主存并通知其他线程保存的 i 值失效, 此时 B 线程需要重新读取 i 的值那么此时 B 线程保存的 i 就是 6,同时 B 线程保存的 temp 还仍然是 6, 然后 B 线程执行 i=temp (6),所以导致了计算结果比预期少了 1。

四、volatile 和 synchronized 的区别

volatile 本质是在告诉 JVM 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的
volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性
volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化

为什么volatile能保证有序性不能保证原子性相关推荐

  1. 7.volatile怎么通过内存屏障保证可见性和有序性?

    volatile通过内存屏障保证可见性 小陈:老王,你上一篇抛出一个问题volatile怎么通过内存屏障保证可见性和有序性?我现在迫不及待的想知道了. 老王:嗯嗯,我们慢慢来讲,先说说volatile ...

  2. setnx是原子操作吗_谈谈Volatile关键字?为什么不能保证原子性?用什么可以替代?为什么?...

    大家好,欢迎关注我的公众号码猿bug,需要资料的话可以加我微信好友. 再谈volatile关键字之前,首先必须聊聊JMM内存模型! JMM主要的特性:可见性.原子性,顺序性 Java 虚拟机规范试图定 ...

  3. volatile能保证有序性

    在前面提到volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性. volatile关键字禁止指令重排序有两层意思: 1)当程序执行到volatile变量的读操作或者写操 ...

  4. Java面试之Synchronized无法禁止指令重排却能保证有序性

    为什么Synchronized无法禁止指令重排,却能保证有序性 前言 首先我们要分析下这道题,这简单的一个问题,其实里面还是包含了很多信息的,要想回答好这个问题,面试者至少要知道一下概念: Java内 ...

  5. volatile对原子性、可见性、有序性的保证

    1.原子性 volatile对原子性的保障有限,32位jvm中的long.double类型的变量赋值操作不是原子的,volatile可以保证它俩的原子性. 2.可见性.有序性 volatile在写操作 ...

  6. synchronized同时对原子性、可见性、有序性的保证

    原子性:基本复制写操作都能保证原子性,复杂操作无法保证 可见性:MESI协议的flush.refresh配合使用,解决可见性 有序性:3个层次,最后1个层次有4中内存重排序 synchronized可 ...

  7. 一道题决定去留:为什么synchronized无法禁止指令重排,却能保证有序性?

    前几天有一位读者找我问一个问题,说是这道题可能影响了他接下来3年的技术成长. 据说这位读者前面的很多问题会的都还可以,属于那种可过可不过的类型的,面试官出了最后一道题,就是回答的满意就可以给Offer ...

  8. 一道大题决定去留:为什么synchronized无法禁止指令重排,却能保证有序性?

    △Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第 253篇原创分享 作者 l Hollis 来源 l Java之道(ID:javaways) 前几天有一位读者找我问一个问题 ...

  9. synchronized能不能保证有序性??

    肯定能啊. 但是: 比如单例模式里面的双检锁dcl,为什么还要加volatile禁止指令重排序呢?? class Singleton{private static Singleton instance ...

  10. x86 CPU内存屏障保证有序性

最新文章

  1. 配置安装光盘为yum源
  2. 商城开发(1)-前期准备
  3. php 负载监控_PHP监控linux服务器负载
  4. 网站PC端跟移动端有哪些不同的区别所在?
  5. 剑指offer 算法 (时间效率)
  6. android网络请求 post
  7. javascript-按圆形排列DIV元素(三)实例---- 图片按椭圆形转动
  8. python for everybody作业和测试答案_Python第九,十章练习题 (第五周作业)
  9. Spring-context-ApplicationEvent/ApplicationListener/ApplicationEventMulticaster
  10. 《在近端对回传音频的检测和抑制》笔记
  11. 在Win10系统的服务器上离线安装SQL Server 2012中出现“启用windows功能NetFx3时出错”
  12. 智能会议系统集成解决方案
  13. 马王堆汉墓帛书‧老子乙本——道经
  14. 腾讯云拟年内在全球增设超30%数据中心;Zepp Health首季成人智能手表出货量居全球前四 | 全球TMT...
  15. 2021年美亚杯资格赛解析
  16. CAN详解--CAN与com口介绍
  17. 一文读懂Jina生态的Dataclass
  18. (附源码)php水果百科动态网站 毕业设计 060917
  19. 机器学习之LASSO,岭回归
  20. 第17章 有关事务的楔子

热门文章

  1. 容器操作系统再添丁,AWS开源Bottlerocket,类似RancherOS?
  2. OpenStack Queens 女王新神器 — 卷多重挂载
  3. 【语音处理】基于matlab音频信号FIR+IIR(高通+低通+带通)滤波器频谱分析【含Matlab源码 1732期】
  4. 【路径规划】基于matlab A_star算法求解机器人栅格地图最短路径规划问题【含Matlab源码 1388期】
  5. 【车牌识别】基于matlab GUI模板匹配车牌识别【含Matlab源码 958期】
  6. 【图像分割】基于matlab超像素图像分割【含Matlab源码 720期】
  7. 【优化预测】基于matlab鲸鱼算法优化LSSVM预测【含Matlab源码 104期】
  8. 【图像分割】基于matlab随机游走算法图像分割【含Matlab源码 149期】
  9. java jlist组件_Java中Jlist的Swing组件
  10. 数字社会的下一波浪潮_下一波创业浪潮是自然而然的