volatile 关键字(修饰变量)

1. 含义

是一种比 sychronized 关键字更轻量级的同步机制,访问 volitile 变量时,不会执行加锁操作。

2. 作用

volatile 是一个类型修饰符(type specifier)。volatile 的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。

  1. 保证可见性
  2. 禁止指令重排序优化

指令重排序优化:普通的变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序和程序代码中的执行顺序一致

3. 如何保证可见性

新值能立即同步到主内存,以及每次使用前立即从主内存刷新。

  • volatile 修饰的变量,是直接拿的主内存的值,就是说这个值永远是最新的,对其他线程是可见的。

  • 而访问非 volatile 变量时,每个线程都会从系统内存(主内存)拷贝变量到工作内存中,然后修改工作内存中的变量值,操控的变量可能不同。

4. 如何禁止指令重排序优化

volatile 通过设置 Java 内存屏障禁止重排序优化。

java 内存屏障

内存屏障也称为内存栅栏或栅栏指令,是一种屏障指令,它使CPU或编译器对屏障指令之前和之后发出的内存操作执行一个排序约束。 这通常意味着在屏障之前发布的操作被保证在屏障之后发布的操作之前执行。

java 的内存屏障通常所谓的四种即LoadLoad,StoreStore,LoadStore,StoreLoad实际上也是上述两种的组合,完成一系列的屏障和数据同步功能。

Load 指令(也就是从内存读取),Store指令 (也就是写入内存)。)

  • LoadLoad 屏障:对于这样的语句 Load1; LoadLoad; Load2 ,在 Load2 及后续读取操作要读取的数据被访问前,保证Load1 要读取的数据被读取完毕。

  • StoreStore 屏障:对于这样的语句 Store1; StoreStore; Store2 ,在 Store2 及后续写入操作执行前,保证 Store1 的写入操作对其它处理器可见。

  • LoadStore 屏障:对于这样的语句 Load1; LoadStore; Store2 ,在 Store2 及后续写入操作被刷出前,保证 Load1 要读取的数据被读取完毕。

  • StoreLoad 屏障:对于这样的语句 Store1; StoreLoad; Load2 ,在 Load2 及后续所有读取操作执行前,保证 Store1 的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能

volatile 做了什么

在一个变量被 volatile 修饰后,JVM 会为我们做两件事:

  1. 在每个 volatile 写操作前插入 StoreStore 屏障,在写操作后插入 StoreLoad 屏障。(StoreStore-写-StoreLoad

  2. 在每个 volatile 读操作前插入 LoadLoad 屏障,在读操作后插入LoadStore屏障。(LoadLoad-读-LoadStore

5. volatile 是不安全的

虽然 volatile 可见性保证了对 volatile 变量所有的写操作都能立刻反应到其他线程之中(即 volatile 变量在各个线程中都是一致的),但是 Java 里面的运算并非原子操作。只有是原子操作的 volatile 变量才是线程安全的,比如我们很常见的 变量++ 自增操作,在这个过程中,自增包括取数,加一,保存三个过程的操作,所以自增并不是原子性操作,使用 volatile 修饰的变量自增操作仍然是不安全的。

举个例子:
public class MyVolitile {

    private static volatile int count = 0;

    public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
count++;
}
}
}).start();
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println(count);
} }

结果每次都不一样,不一定等于 200。

6. volatile 不适用场景

由于 volatile 变量只能保证可见性,在不符合以下两条规则的运算场景中,我们仍然要通过加锁(使用 synchronized 或 java.util.concurrent 中的原子类)来保证原子性。

  • 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值
  • 变量不需要与其他的状态变量共同参与不变约束

volatile 关键字(修饰变量)的更多相关文章

  1. Java学习笔记18---final关键字修饰变量、方法及类

    英语里final这个单词大家都知道是"最终的"意思,其实还有一个意思是"不可更改的".在Java里,final关键字作"不可更改的"来解释更 ...

  2. Java中final关键字修饰变量、方法、类的含义是什么

    Java中的关键字final修饰变量.方法.类分别表示什么含义? 先看一个简单的介绍 修饰对象 解释说明 备注 类 无子类,不可以被继承,更不可能被重写. final类中的方法默认是final的 方法 ...

  3. 使用 volatile 关键字保证变量可见性和禁止指令重排序

    volatile 概述 volatile 是 Java 提供的一种轻量级的同步机制.相比于传统的 synchronize,虽然 volatile 能实现的同步性要差一些,但开销更低,因为它不会引起频繁 ...

  4. java的final关键字——修饰变量

    final修饰的变量不可变,指的是引用不可变,(除基本类型)而不是内容. final修饰的成员变量必须被初始化

  5. Java并发编程:JMM和volatile关键字

    转载请标明出处: http://blog.csdn.net/forezp/article/details/77580491 本文出自方志朋的博客 Java内存模型 随着计算机的CPU的飞速发展,CPU ...

  6. 【JUC系列第一篇】-Volatile关键字及内存可见性

    作者:毕来生 微信:878799579 什么是JUC? JUC全称 java.util.concurrent 是在并发编程中很常用的实用工具类 2.Volatile关键字 1.如果一个变量被volat ...

  7. 架构师养成记--4.volatile关键字

    volatile修饰的变量可在多个线程间可见. 如下代码,在子线程运行期间主线程修改属性值并不对子线程产生影响,原因是子线程有自己独立的内存空间,其中有主内存中的变量副本. public class ...

  8. volatile关键字及编译器指令乱序总结

    本文简单介绍volatile关键字的使用,进而引出编译期间内存乱序的问题,并介绍了有效防止编译器内存乱序所带来的问题的解决方法,文中简单提了下CPU指令乱序的现象,但并没有深入讨论. 以下是我搭建的博 ...

  9. Java单例模式和volatile关键字

    单例模式是最简单的设计模式,实现也非常"简单".一直以为我写没有问题,直到被 Coverity 打脸. 1. 暴露问题 前段时间,有段代码被 Coverity 警告了,简化一下代码 ...

  10. [其他]volatile 关键字

    用  volatile 关键字修饰函数 的作用是 告诉编译器该函数不会返回 , 让编译器能产生更好的代码 另外也能避免一些假警告信息,如未初始化的变量等

随机推荐

  1. java课后实验性问题1

    一.一个java类文件中只能有一个公有类吗? 测试代码 public class Test{ public static void main(String[] args){ } public clas ...

  2. powerdesigner导出rtf

    报告--reports--新建reports--reports template选择None 从左侧的Available items 拖动 需要导出的item到右侧 右侧的每一个item右键 可以设置 ...

  3. jquery - 定义二维数组

    var products = []; products.push({product_id: '1',count: 3},{product_id:'2',count: 6})

  4. Vue于React特性对比(三)

    最近重学React,再次和vue做了对比. 一,为官方插件提供便利的第三方插件横行 React仅仅是一个ui框架.虽然官方提供了redux,react-router:但也有第三方的redux-thun ...

  5. 【I·M·U_Ops】------Ⅱ------ IMU自动化运维平台之CMDB

    说明本脚本仅作为学习使用,请勿用于任何商业用途.本文为原创,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明. #A 我理解的 CMDB CMDB翻译过来,Configuratio ...

  6. centos6.5安装mysql(转载,亲测可用)

    如果要在Linux上做j2ee开发,首先得搭建好j2ee的开发环境,包括了jdk.tomcat.eclipse的安装(这个在之前的一篇随笔中已经有详细讲解了Linux学习之CentOS(七)--Cen ...

  7. Smarty 获取当前日期时间和格式化日期时间

    在Smarty 中获取当前日期时间和格式化日期时间与PHP中有些不同的地方,这里就为您详细介绍: 首先是获取当前的日期时间:在PHP中我们会使用date函数来获取当前的时间,实例代码如下:date(& ...

  8. iOS开发之—— 加密使用(MD5,base64,DES,AES)

    基本的单向加密算法: BASE64 严格地说,属于编码格式,而非加密算法 MD5(Message Digest algorithm 5,信息摘要算法)SHA(Secure Hash Algorithm ...

  9. c# 子窗体居中父窗体

    1.设置CenterParent不管用.只好用代码控制. frmRunning_ = new FrmRunning(); frmRunning_.StartPosition = FormStartPo ...

  10. Flutter 的一些小技巧

    1. 获取状态栏高度 import 'dart:ui'; MediaQueryData.fromWindow(window).padding.top 2. 设置AppBar的高度 Scaffold( ...