前言

在Java并发编程中,volatile关键字有着至关重要的作用,在面试中也常常会是必备的一个问题。本文将会介绍volatile关键字的作用以及其实现原理。

volatile作用

volatile在并发编程中扮演着重要的角色,volatile是轻量级的synchronized,volatile关键字有两个作用:

1)保证共享变量的可见性

可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。笔者此前一篇文章Java并发编程:Java内存模型JMM中有说到,Java内存模型中有主内存和本地内存之分,本地内存持有共享变量的一份副本,线程对共享变量的修改是先修改本地内存的副本,然后再回写到主内存中去。

可能存在这样的情况,线程A和线程B同时去修改一个共享变量C,假设线程A先对共享变量C做了修改,而此时线程B却没能及时感知到共享变量C已经发生了改变,紧接着B对本地过期的副本数据进行了修改,这造成了共享变量的不可见问题。

而使用了volatile关键字修改的共享变量,当线程修改了共享变量之后,会立马刷新到主内存中,并且会使其他线程缓存了该地址的数据失效,这就保证了线程之间共享变量的可见性。

2)防止指令重排序

volatile关键字的另外一个作用就是防止指令重排序。代码在实际执行过程中,并不全是按照编写的顺序进行执行的,在保证单线程执行结果不变的情况下,编译器或者CPU可能会对指令进行重排序,以提高程序的执行效率。但是在多线程的情况下,指令重排序可能会造成一些问题,最常见的就是双重校验锁单例模式:

public class SingletonSafe {

    private static volatile SingletonSafe singleton;

    private SingletonSafe() {
} public static SingletonSafe getSingleton() {
if (singleton == null) {
synchronized (SingletonSafe.class) {
if (singleton == null) {
singleton = new SingletonSafe();
}
}
}
return singleton;
}
}

如果没有使用volatile关键字,则可能会出现其他线程获取了一个未初始化完成的singleton对象,具体原因笔者不在这里赘述了,有兴趣地同学可以搜索一下“double checked locking with delay initialization”学习下,笔者后续有时间再写篇文章分析下。

volatile实现原理

1)可见性实现原理

对于volatile关键字修饰的变量,当对volatile变量进行写操作的时候,JVM会向处理器发送一条lock前缀的指令,将这个缓存中的变量回写到系统主存中。但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议。

缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。

所以,如果一个变量被volatile所修饰的话,在每次数据变化之后,其值都会被强制刷入主存。而其他处理器的缓存由于遵守了缓存一致性协议,也会把这个变量的值从主存加载到自己的缓存中。这就保证了一个volatile在并发编程中,其值在多个缓存中是可见的。

2)防止指令重排序实现原理

volatile防止指令重排序是通过内存屏障来实现的。内存屏障分为如下三种:

Store Barrier

Store屏障,是x86的”sfence“指令,强制所有在store屏障指令之前的store指令,都在该store屏障指令执行之前被执行。

Load Barrier

Load屏障,是x86上的”ifence“指令,强制所有在load屏障指令之后的load指令,都在该load屏障指令执行之后被执行

Full Barrier

Full屏障,是x86上的”mfence“指令,复合了load和save屏障的功能。

Java内存模型中volatile变量在写操作之后会插入一个store屏障,在读操作之前会插入一个load屏障。一个类的final字段会在初始化后插入一个store屏障,来确保final字段在构造函数初始化完成并可被使用时可见。也正是JMM在volatile变量读写前后都插入了内存屏障指令,进而保证了指令的顺序执行。

原创声明

本文发布于掘金号【Happyjava】。Happy的掘金地址:https://juejin.im/user/5cc2895df265da03a630ddca,Happy的个人博客:http://blog.happyjava.cn。欢迎转载,但须保留此段声明。

关注公众号领资料

搜索公众号【Happyjava】,回复【电子书】和【视频】,即可获取大量优质电子书和大数据、kafka、nginx、MySQL等视频资料

[面试必备]深入理解Java的volatile关键字的更多相关文章

  1. Java面试官最爱问的volatile关键字

    在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...

  2. 【转】java中volatile关键字的含义

    java中volatile关键字的含义   在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言 ...

  3. 转:java中volatile关键字的含义

    转:java中volatile关键字的含义 在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言 ...

  4. Java中Volatile关键字详解 (转自郑州的文武)

    java中volatile关键字的含义:http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html 一.基本概念 先补充一下概念:J ...

  5. Java面试官最常问的volatile关键字

    在Java相关的职位面试中,很多Java面试官都喜欢考察应聘者对Java并发的了解程度,以volatile关键字为切入点,往往会问到底,Java内存模型(JMM)和Java并发编程的一些特点都会被牵扯 ...

  6. 对Java单例模式 volatile关键字作用的理解

    单例模式是程序设计中经常用到的,简单便捷的设计模式,也是很多程序猿对设计模式入门的第一节课.其中最经典的一种写法是: class Singleton { private volatile static ...

  7. Java中volatile关键字你真的理解了吗?

    面:你怎样理解volatile关键字时? 我:不加思索的说出,volatile修饰的成员变量,可保证线程可见性.不保证原子性和禁止指令重排. 面:你能谈谈什么是线程可见性吗? 我:各个线程对主内存中共 ...

  8. java中volatile关键字的理解

    一.基本概念 Java 内存模型中的可见性.原子性和有序性.可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉.通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有 ...

  9. 【面试必备】常见Java面试题大综合

    一.Java基础 1.Arrays.sort实现原理和Collections.sort实现原理答:Collections.sort方法底层会调用Arrays.sort方法,底层实现都是TimeSort ...

随机推荐

  1. 051_switch语句的使用 052_while循环详解 053_for循环详解_dowhile简介 054_嵌套循环_循环相关练习

    051_switch语句的使用 package testmode2;/** * 测试switch语句 * 遇到多值判断的时候,使用switch.当然,switch完全可以使用ifelseifelse代 ...

  2. Linux中通配符

    通配符是由shell处理的, 它只会出现在 命令的“参数”里.当shell在“参数”中遇到了通配符时,shell会将其当作路径或文件名去在磁盘上搜寻可能的匹配:若符合要求的匹配存在,则进行代换(路径扩 ...

  3. 【原】shell增加定时任务

    通过shell脚本增加定时任务:#CRON_FILE="/var/spool/cron/root" (centos系统) CRON_FILE="/var/spool/cr ...

  4. RPA_播放语音

    验证码识别 from rpa.captcha.captcha import Captcha c = Captcha() log.info(tmp_file_path) captcha_result = ...

  5. mysql 数据库中的模式替换

    1, REPLACE 使用 -UPDATE tbl_name SET field_name = REPLACE(field_name,old_str,new_str) WHERE conditions ...

  6. 在Docker中使用Microsoft SQL Server数据库

    下图中对SQL Server容器创建及数据库创建等操作进行了记录,方便自己日后查看.(文中的 * 仅表示隐藏自己的个人信息,手动马赛克,哈哈-) Docker下载可看上一篇博文mac系统,docker ...

  7. Git - git bash 在 windows 下创建软连接

    1. 概述 使用 git bash 在 windows 下创建软连接 或者叫 快捷方式 感谢 Tony 老师的帮助 Tony 的技术笔记 Windows 使用 ln -s 创建软链接 2. 问题 需求 ...

  8. yii2 components

    components顾名思义就是组件的意思,yii默认会根据 components 数组里面的键值去 vendor\yiisoft\yii2\web 里面查找这个键值得类,如果没有找到,再根据这个键值 ...

  9. ARM架构安装Anaconda3出现错误:cannot execute binary file: Exec format error

    ARM架构安装Anaconda3出现错误:cannot execute binary file: Exec format error 原因是:安装包格式不对. 在Anaconda官网上只有x86(32 ...

  10. (原创)Windows下编译的Shell脚本不能再Linux中运行的解决办法

    一.原理 Windows编译的文件和Linux编译的文件格式不太一样,导致在Linux运行Shell脚本的时候会提示:/bin/bash^M: bad interpreter: 没有那个文件或目录. ...