大概是因为项目、业务的原因,工作上几乎还没有使用过多线程相关的功能,相关知识差不多都忘了,所以最近补一下基础。

  volatile用来修饰共享变量,volatile变量具有 synchronized 的可见性特性,但是不具备原子特性。volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。当成员变量发生变化时,强迫线程将变化值回写到共享内存。以保证在同一时刻,不同的线程看到该成员变量的值是一致的。

  因为volatile关键字与Java的内存模型有关,所以先看一下Java内存模型里面的几个概念知识。

  1、可见性

  指多个线程访问同一个变量时,一个线程修改了该变量的值,其他线程立马可以看到该线程修改后的值。

  如下图所示,每个线程都有自己的工作内存,线程访问共享变量时,会先将变量加载到工作内存(如工作内存未缓存该变量),然后对工作内存中的变量副本进行读写操作。假设多个线程同时访问a变量,线程1修改了工作内存中的a变量值,但没有刷新到主内存中,其他线程从主内存缓存的a变量值还是原来的旧值;或者线程1修改a变量的值后刷新到了主内存,但是其他线程工作内存中缓存了线程1修改之前的旧值,其他线程访问该变量时没有从主内存中重新获取,就会出现在多个线程中,同一共享变量的值不一致,即存在可见性问题。

  在 Java 中 volatile、synchronized 和 final 实现可见性。

  

  2、原子性

  同数据库的原子性,指一个不可拆分的最小单位,原子操作表示一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么全部不执行。

  比如:i = 0;(i非long和double类型)这个操作是不可分割的,那么我们说这个操作是原子操作。

  再比如:i++;实际上等于i = i + 1;先读取i的值,再修改i的值,是可分割的两个操作,所以不是原子操作。

  在 Java 中 synchronized 和在 lock、unlock 中操作保证原子性。

  3、有序性

  指程序按照代码的先后顺序执行。

  《深入理解Java虚拟机》中提到:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。前半句是指“线程内表现为串行语义”,后半句是指“指令重排序”现象和“工作内存中主内存同步延迟”现象。Java 语言提供了 volatile和synchronized两个关键字来保证线程之间操作的有序性,volatile是因为其本身就包含了“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个锁的两个同步块只能串行地进入。


一、volatile实现原理

  volatile是轻量级的synchronized,如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。volatile变量修饰的共享变量在进行写操作时,会多一行带有lock前缀的汇编指令,lock前缀的指令在多核处理器下会引发两件事:

  1、  将当前处理器缓存行的数据写回到系统内存。

  Lock前缀指令会引起处理器缓存回写到内存。Lock前缀指令导致在执行指令期间,声言处理器的LOCK#信号。在多处理器环境中,LOCK#信号确保在声言该信号期间,处理器可以独占任何共享内存。

  2、  写回内存的操作会使其他CPU里缓存了该内存地址的数据无效。

  IA-32处理器和Intel 64处 理器使用MESI(修改、独占、共享、无效)控制协议去维护内部缓存和其他处理器缓存的一致性。处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致。

二、volatile内存语义

  当声明共享变量为volatile后,就具备了以下两层语义:

  1、保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

  2、禁止进行指令重排序。

  从内存语义的角度来说,volatile的写-读与锁的释放-获取有相同的内存效果:volatile写和锁的释放有相同的内存语义;volatile读与锁的获取有相同的内存语义。

  当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。

  当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

  为了优化程序性能,编译器重和处理器会对指令序列进行重新排序。但是为了实现volatile内存语义,JMM会分别限制这两种类型的重排序类型。编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

三、volatile使用场景

  虽然说volatile是轻量级的synchronized,但是volatile关键字是无法替代synchronized关键字的。synchronized关键字是防止多个线程同时执行一段代码,会很影响程序执行效率,但是能够保证一组操作的原子性;而volatile对任意单个变量的读/写具有原子性,但对于一组复合操作不具有原子性(例如:i++)。

  要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:

  • 对变量的写操作不依赖于当前值。
  • 该变量没有包含在具有其他变量的不变式中。

Java并发编程之volatile关键字的更多相关文章

  1. Java并发编程之volatile关键字解析

    一内存模型的相关概念 二并发编程中的三个概念 三Java内存模型 四深入剖析volatile关键字 五使用volatile关键字的场景 volatile这个关键字可能很多朋友都听说过,或许也都用过.在 ...

  2. Java 并发编程之volatile关键字解析

    摘录 1. 计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入.由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执 ...

  3. Java并发编程之synchronized关键字

    整理一下synchronized关键字相关的知识点. 在多线程并发编程中synchronized扮演着相当重要的角色,synchronized关键字是用来控制线程同步的,可以保证在同一个时刻,只有一个 ...

  4. java并发编程之volatile

    Java语言规范第三版中对volatile的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量. 了解volatile关键字之 ...

  5. Java并发编程之volatile的应用

    在多线程的并发编程中synchronized和volatile都扮演着重要的角色.volatile是轻量级的synchronized,它在多处理器的开发中保证了共享变量的可见性,可见性的意思是当一个线 ...

  6. Java并发编程之volatile变量

    volatile提供了弱同步机制,用来确保将变量更新通知到其它线程.volatile变量不会被缓存在寄存器中或者对其它处理器不可见的地方,因此在读取volatile变量时总会返回最新写入的值.可以想象 ...

  7. Java并发编程之CAS

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...

  8. Java并发编程之CAS第一篇-什么是CAS

    Java并发编程之CAS第一篇-什么是CAS 通过前面几篇的学习,我们对并发编程两个高频知识点了解了其中的一个—volatitl.从这一篇文章开始,我们将要学习另一个知识点—CAS.本篇是<凯哥 ...

  9. Java并发编程之CAS二源码追根溯源

    Java并发编程之CAS二源码追根溯源 在上一篇文章中,我们知道了什么是CAS以及CAS的执行流程,在本篇文章中,我们将跟着源码一步一步的查看CAS最底层实现原理. 本篇是<凯哥(凯哥Java: ...

随机推荐

  1. [leetcode]57. Insert Interval插入区间

    Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessa ...

  2. 30.Mysql常见问题和应用技巧

    30.Mysql常见问题和应用技巧30.1 忘记Mysql的root密码30.2 如何处理MyISAM存储引擎的表损坏 30.2.1 方法一:使用myisamchk工具 30.2.2 方法二:使用SQ ...

  3. 学习UI设计书籍推荐

    在学习UI设计的过程当中,特别想学或者零基础的人来说,需要学习到很多知识,比如软件 PS AI ,理论 色彩 排版 规范 UE 等,这些都是一名UI设计师需要学习的知识,而学习到这些知识,可以通过视频 ...

  4. 分布式协议学习笔记(一) Raft 选举

    Raft官网 官方可视化动画1 官方可视化动画2 论文中文翻译 论文英文地址 感觉作为paxos的升级精简版 Raft在设计之初就以容易理解为目标 看完资料 脑海里都有了大概的轮廓. 有了这些详细的资 ...

  5. 用windows性能监视器检测sqlserver 常见指标

    转载地址:https://www.cnblogs.com/xdong/p/4296072.html

  6. flex布局之flex-basis采坑

    场景: 容器设置为display: flex,容器里的子项目部分设置 flex: auto,子项目的宽高自适应正常,但如果再往子项目里嵌套一个(如:div),并设置高度(如:height: 100%) ...

  7. Scrum冲刺阶段2

    成员今日完成的任务 人员 任务 何承华 后端设计 陈宇 后端设计 丁培辉 后端设计 温志铭 主页面的设计 杨宇潇 主页面的设计 张主强 服务器构建 成员遇到的问题 人员 问题 何承华 暂无 陈宇 暂无 ...

  8. jedis set 的四个重载方法(byte[]的四个自动忽略)

    方法定义如下: 1.String set(String key, String value) 2.String set(String key, String value, String nxxx) 3 ...

  9. s5 Docker的持久化存储和数据共享

    数据库容器的数据如何才能不会丢失?Docker的持久化存储技术.Docker的数据共享技术能极大提高开发人员的开发效率,边写代码,边看运行结果. 数据持久化之Data Volume Docker持久化 ...

  10. telnet的安装和使用

    在日常使用中,有时候需要检测服务器上面的部分端口有没有打开,这个时候可以使用telnet进行调试.下面是一篇转载的文章. 原文地址:http://linuxtech.blog.51cto.com/36 ...