目录

一、简介

二、volatile使用场景 - 保证可见性

三、volatile使用场景 - 禁止指令重排序

四、volatile使用场景 - 不保证原子性

五、总结


一、简介

volatile,是Java中的一个关键字。被volatile修饰的变量,在多个线程中保持可见性,注意,volatile不保证原子性,这也是volatile与synchronized的区别之一。

volatile关键字只保证可见性,不能保证原子性。

  • 什么是可见性?

可见性是指当多个线程访问同一个变量时,一个线程如果修改了这个变量的值,其他线程能够立即看得到修改之后的值。

在了解volatile的工作原理之前,首先需要了解一下Java内存模型,如下图:

在Java中,每个线程都有一个独立的内存空间,称为工作内存。 它保存了用于执行操作的不同变量的值。在执行操作之后,线程将变量的更新值复制到主内存中,这样其他线程可以从主内存中读取最新值。

二、volatile使用场景 - 保证可见性

如果需要保证某个变量在多个线程之间可见性的时候,可以使用volatile关键字进行修饰。我们来看一个例子:

public class T03_Volatile {/*** 如果没有加volatile修饰,线程A由于死循环,可能没有及时从主内存读取最新的running值* 加了volatile修饰,一旦running的值发生变化,就会通知其他线程需要从主内存重新获取值*/private volatile boolean running = true;private void m1() {while (running) {System.out.println("hello....");}}public static void main(String[] args) {T03_Volatile t03_volatile = new T03_Volatile();new Thread(t03_volatile::m1, "A").start();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}t03_volatile.running = false;}
}

注意:上述程序有可能会出现在没加volatile关键字的情况下,线程A也能及时读取到最新的running值,这主要是由于CPU可能暂时空闲,自动从主内存同步了最新的running到线程A中,导致线程A执行结束。

三、volatile使用场景 - 禁止指令重排序

指令重排序,简单理解就是说,保证代码按照我们写的顺序执行。被volatile修饰了的变量,禁止了指令进行重排序,所以可以保证代码完全按照我们编写的顺序执行(不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的)。

比较典型的例子就是双重检查机制的单例模式,简称DCL单例,具体代码如下:

高频面试题:DCL(Double Check Lock)双重检查单例模式,为什么需要加volatile关键字?主要考点其实就是volatile禁止指令重排。

public class T03_Volatile {/*** 加入volatile关键字修饰*/private volatile static T03_Volatile instance = null;private T03_Volatile() {}public static T03_Volatile getInstance() {if (null == instance) {synchronized (T03_Volatile.class) {if (null == instance) {instance = new T03_Volatile();}}}return instance;}
}

注意我们创建实例的语句 instance = new T03_Volatile(),实际上底层分为三个步骤:

  • 1、分配对象的内存空间;
  • 2、初始化对象;
  • 3、设置实例对象指向刚分配的内存地址;

分析:

步骤2【初始化对象】需要依赖于步骤1【分配对象的内存空间】,但是步骤3【设置实例对象指向刚分配的内存地址】不需要依赖步骤2【初始化对象】,所以有可能出现1,3,2的执行顺序,当出现这种顺序的时候,虽然instance不为空,但是此时对象有可能没有正确初始化,直接拿来使用的话可能会报错。

四、volatile使用场景 - 不保证原子性

volatile关键字不能保证原子性。也就是说,对volatile修饰的变量进行的操作,不保证多线程安全。我们看一个案例:

public class T03_Volatile {private static CountDownLatch countDownLatch = new CountDownLatch(500);private volatile static int num = 0;public static void main(String[] args) {for (int i = 0; i < 500; i++) {new Thread(() -> {try {TimeUnit.MICROSECONDS.sleep(400);num++;} catch (Exception e) {e.printStackTrace();} finally {countDownLatch.countDown();}}).start();}try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("num = " + num);}
}

运行结果:多运行几次,发现结果不固定,有时候会是500,有时候是495,有时候是497,这就证明了volatile不保证原子性。

原因解析:

num++操作并不是原子操作,实际上num++的操作分成了三个步骤进行:

//获取变量的值
int temp = num;
//将该变量的值+1
num = num + 1;
//将该变量的值写回到对应的主内存中
num = temp;

举一个例子:

假设线程A首次拿到的num = 3,在执行+1操作前,可能存在其他多个线程已经对num做了修改,假设此时主内存中的num已经被修改到20了,而此时线程A执行+1操作,将num=3+1=4的结果又重新写回到了主内存中,将原本num应该是num = 20 + 1 = 21的,被线程A回写之后覆盖成了4,因此总的结果小于或者等于500,这就存在了原子性问题。

五、总结

本篇文章介绍了volatile关键字的基本用法,以及通过案例讲解了volatile在内存可见性、指令重排序方面的作用。总结一句话:

volatile关键字保证内存可见性,能禁止指令重排序,但是注意它不保证原子性,所以volatile不能完全替代synchronized关键字,因为synchronized保证原子性的。

并发编程学习之volatile关键字相关推荐

  1. 并发编程系列之volatile关键字详解

    并发编程系列之volatile关键字详解 1.volatile是什么? 首先简单说一下,volatile是什么?volatile是Java中的一个关键字,也是一种同步机制.volatile为了保证变量 ...

  2. Java并发编程学习笔记——volatile与synchronized关键字原理及使用

    Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令. 一.vo ...

  3. 【Java并发编程 】同步——volatile 关键字

    英 /ˈvɒlətaɪl/ 我了太噢(记不住单词怎么读) 一.volatile的介绍? volatile是一个轻量级的synchronized,一般作用与变量,在多处理器开发的过程中保证了内存的可见性 ...

  4. 并发编程基础之volatile关键字的用法

    一:概念 volatile关键字是一个轻量级的线程同步,它可以保证线程之间对于共享变量的同步,假设有两个线程a和b, 它们都可以访问一个成员变量,当a修改成员变量的值的时候,要保证b也能够取得成员变量 ...

  5. libevent c++高并发网络编程_高并发编程学习(2)——线程通信详解

    前序文章 高并发编程学习(1)--并发基础 - https://www.wmyskxz.com/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-j ...

  6. 高并发编程学习(2)——线程通信详解

    前序文章 高并发编程学习(1)--并发基础 - https://www.wmyskxz.com/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-j ...

  7. java中解决脏读_java并发编程学习之脏读代码示例及处理

    使用interrupt()中断线程     当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一个标志,表示它已经被中断,并立即 ...

  8. java volatile 原子性_Java并发编程之验证volatile不能保证原子性

    Java并发编程之验证volatile不能保证原子性 通过系列文章的学习,凯哥已经介绍了volatile的三大特性.1:保证可见性 2:不保证原子性 3:保证顺序.那么怎么来验证可见性呢?本文凯哥(凯 ...

  9. Java高并发编程学习(三)java.util.concurrent包

    简介 我们已经学习了形成Java并发程序设计基础的底层构建块,但对于实际编程来说,应该尽可能远离底层结构.使用由并发处理的专业人士实现的较高层次的结构要方便得多.要安全得多.例如,对于许多线程问题,可 ...

  10. java并发编程学习一

    java并发编程学习一 什么是进程和线程? 进程是操作系统进行资源分配的最小单位 进程跟进程之间的资源是隔离的,同一个进程之中的线程可以共享进程的资源. 线程是进程的一个实体,是CPU 调度和分派的基 ...

最新文章

  1. 微信小程序scroll-view的使用
  2. linux下tomcat安装
  3. CentOS6.8安装Python3.6.3
  4. SAP Spartacus部署到SAP Commerce Cloud,不同的系统设置不同的OCC Base url
  5. python二维散点分布图_深入理解皮尔逊相关系数amp;python代码
  6. 学以致用十三-----Centos7.2+python3+YouCompleteMe成功历程
  7. linux对perl脚本加密,对Perl代码进行编译与加密
  8. mysql or 短路_MySQL是否使IF()函数短路?
  9. “echo ”和“echo ”的区别
  10. VS2010中常用的快捷键
  11. qmc转码_QQ音乐qmc3格式转换器免费版
  12. 【清华大学陈渝】 第十一章_死锁
  13. Win32串口API
  14. fmod文档解析音频_将音频插入Word 2007文档
  15. 单目标多目标优化算法的测试函数与解
  16. 商用密码应用与安全性评估之(一)网络空间安全形式与商用密码工作
  17. TIL:创建Java线程的两种方法
  18. APISpace 分钟级降水预报API
  19. 发送电子邮件响应服务器554 5.7.1,错误 (554 5.2.2 邮箱) 邮箱中启用邮件的公用文件夹Office 365 | Microsoft Docs...
  20. python商品打折问题公式_python障碍式期权定价公式

热门文章

  1. 电脑取消撤销快捷键是什么_必须掌握的十个电脑使用技巧
  2. 阿里云云计算 17 块存储的分类
  3. 算法:判断对称树 101. Symmetric Tree
  4. Mac OS 开启三指拖移,三指缩放,拖拽窗口,切换全面页面变成四指
  5. 算法:Valid Sudoku(有效的数独)
  6. 调制方式性能比较--BER,频带效率的极限
  7. 提交不了_领导嘲讽程序员代码太过整洁,网友:太矫情,这种代码提交不了
  8. android studio | openGL es 3.0增强现实(AR)开发 (2) .so文件的应用和理解
  9. 【PRML 学习笔记】第二章 - 概率分布 (Probability Distributions)
  10. 凸优化学习笔记(四):对偶性、KKT 条件、敏感性分析