原文连接:(http://www.studyshare.cn/blog/details/1163/0 )

一、volatile定义

volatile是java并发编程中修饰类的成员变量、成员属性或者对象的一个关键字。是java并发编程中最轻量级的并发实现,保证所修饰的变量对多个线程内存可见。在一个线程写,多个线程读的场景下,首选使用volatile关键字。

java开发工具下载地址及安装教程大全,点这里
更多技术文章,在这里

二、使用案例

此处用一个线程安全的单例模式来说明volatile具体的使用场景和原理,首先,线程安全的单例模式是很多面试官都喜欢考察面试者的一个问题,网络上各种博客都谈到饿汉式,懒汉式单例模式的编写方式,也提到过一个双重检查模式的单例模式,双重检查单例模式代码如下:

相信很多人认为这已经是线程安全的单例模式了,然而这真的是线程安全的吗?其实不是,为什么呢?原因其实很简单,如果两个线程调用getInstance()方法的时候,其中某一个线程正在执行第一重检查,另外一个线程正在执行第二重检查中的instance = new Singlton()的时候,此时这句实例化的代码在我们看来只有一句,但翻译成cpu能运行的指令的时候会变成三句:(1)给instance分配一个栈指针,(2)给Singlton对象分配堆内存,(3)将栈指针指向堆内存。那么cpu在做这三步操作的时候执行顺序会重排序(cpu按自己的规则重新排序后执行),即执行顺序可能是(1)(3)(2),那么问题就来了,当执行(1)后,假如第一个线程正在执行第一重检查,那么此时会发现instance并不为空,就直接返回了。然而实际对象分配堆内存还没完成,此时就会造成instance只是一个空的指针。当我们调用Singlton对象中的成员变量时,就会出错。问题出来了,那么解决方法非常简单,就是使用该文章的主题--volatile关键字,在我们对象成员变量上加上volatile即可。

private static volatile Singlton instance ;

三、深入剖析

  1. 要理解透彻volatile的原理需要明白cpu指令重排序、内存屏障以及线程的本地缓存和主内存等相关知识。指令重排序:以上的示例也说明了cpu在执行一条我们编写的代码的时候,首先会编译成汇编语言,最终编译成二进制指令后在cpu的寄存器上进行存取并执行。即一条代码会变成n条指令,cpu执行这n条指令的时候,不会老老实实的按顺序进行执行,而是会按一定的规则(可能是先简单后复杂,
  1. 先加减后乘除等等)重新排序后执行。这就是指令重排序。重排序的类型有编译器优化重排序,指令级并行重排序,内存系统重排序。
  1. 内存屏障:这是针对并发下重排序带来的问题的一种解决方案,现代cpu都有的一种内存屏障类型是:StoreLoad Barriers,另外还有三种分别是:LoadLoad BarriersStoreStore Barriers以及LoadStore Barriers.

  1. 线程的本地缓存与主内存:

  1. 1)、每个线程都会有自己的本地缓存,如上图所示,线程A,线程B都会有自己的本地缓存,用来缓存线程本身所需要使用的数据。
  1. 2)、正常情况下,当线程A改变了自己本地缓存中的a变量的值,并不会马上刷新到主内存,同理,线程B也一样。
  1. 3)、如果给a变量加上volatile修饰,那么当线程A改变了本地缓存中a的值后,cpu会强制将线程A的本地缓存中的a变量的值刷新到主内存,即让主内存的a的值与线程A的本地缓存中的a值一致。且同时让线程B中的本地缓存中的a失效,那么当线程B要使用本地缓存中的a变量的时候,会重新到主内存读取并刷新自己的本地缓存。这就是volatile的内存语义。然后回头解释案例中为何加入volatile修饰就保证了真正的线程安全,直接来说就是一句话:volatile修饰的变量在cpu底层执行的时候插入了相关的内存屏障禁止指令进行重排序。具体来说是在volalite写操作前插入StoreStore屏障,写操作后面插入StoreLoad屏障,在volatile读操作的后面连续插入LoadLoad屏障和LoadStore屏障。
    总结volatile关键字修饰的变量具有内存可见性,对一个volatile变量的读,总是能看到(任意线程)对这个变量最后的写入。volatile的并发实现是利用cpulock前缀指令对消息总线进行加锁,同时让volatile当前所在线程的本地缓存内容强行刷进主内存,并让其他使用了该变量的本地缓存失效,当其他线程需要使用volatile所修饰的变量的时候就会重新到主内存中读取。
  1. 本文为博主原创文章,欢迎留言交流,转载请注明原文出处。
    java开发工具下载地址及安装教程大全,点这里
    更多技术文章,在这里

volatile-最轻量级的并发实现及其内存语义的更多相关文章

  1. Java并发编程原理与实战四十二:锁与volatile的内存语义

    锁与volatile的内存语义 1.锁的内存语义 2.volatile内存语义 3.synchronized内存语义 4.Lock与synchronized的区别 5.ReentrantLock源码实 ...

  2. 【java多线程系列】java中的volatile的内存语义

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

  3. 内存屏障和volatile内存语义的实现

    趁周末,把以前的书拿出来,再翻一番,顺便做个笔记: 内存屏障:用来控制和规范cpu对内存操作的顺序的cpu指令. 内存屏障列表: 1.loadload:确保“前者数据装载”先于“后者装载指令”: 2. ...

  4. JAVA锁和volatile的内存语义&volatile的使用场景

    JAVA锁的内存语义 当线程释放锁时,JMM(Java Memory Model)会把该线程对应的本地内存中的共享变量刷新到主内存中. 当线程获取锁时,JMM会将该线程对应的本地内存置为无效.从而使得 ...

  5. volatile 和锁的内存语义

    一.volatile 的内存语义 1. volatile 的特性 volatile变量自身具有以下特性: 可见性 :对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后 ...

  6. volatile的内存语义与应用

    volatile的内存语义 volatile的特性 理解volatile特性的一个好方法是把对volatile变量的单个读/写,堪称是使用同一个锁对这些单个读/写操作做了同步. 锁的happens-b ...

  7. 基础篇:深入JMM内存模型解析volatile、synchronized的内存语义

    目录 1 java内存模型,JMM(JAVA Memory Model) 2 CPU高速缓存.MESI协议 3 指令重排序和内存屏障指令 4 happen-before原则 5 synchronize ...

  8. Java内存模型(MESI、内存屏障、volatile和锁及final内存语义)

    JMM (Java内存模型) Java线程的实现 实现线程主要有三种方式,Java线程从JDK1.3后采用第一种方式实现: 使用内核线程实现(1:1实现) 使用用户线程实现(1:N实现) 使用用户线程 ...

  9. volatile特性及内存语义

    1.volatile变量自身具有下列特性:·可见性:对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入.·原子性:对任意单个volatile变量的读/写具有原子 ...

随机推荐

  1. Eclipse安装代码反编译插件Enhanced Class Decompiler

    在开发过程中,如果想查看引入资源的源代码,可以借助eclipse的插件Enhanced Class Decompiler轻松实现,下面我来讲解一下如何安装使用这个插件. 1.打开Eclipse菜单-& ...

  2. XML 中 5 个预定义的实体引用

    < < 小于 > > 大于 & & 和号 &apos; ' 省略号 " " 引号

  3. RockBrain USB Server外设虚拟化高可用解决方案(银企直联虚拟化解决方案)

    技术指标: 单.双千兆网络界面(支持链路冗余与链路热备.支持双网口均衡负载) 原生USB2.0接口(USB2.0与USB3.0接口均对所有USB版本设备兼容,支持混插) 技术优势: RockBrain ...

  4. SNF快速开发平台2019-用户安全控制-权限管理模型实践-权限都在这里

    1.1    是否保存密码 勾选记住密码后,再次开启程序用户密码不需要再次输入,直接显示在密码输入框内,方便快捷. 图 4.1‑1 记住密码的登录页面框 1.2    是否自动登录 勾选自动登录后,再 ...

  5. ES6 - 解构(数组和对象)

    解构对象 /** * 解构:快捷,方便 * * 对象解构 */ { var expense = { type: "es6", amount: "45" }; / ...

  6. Jenkins 发布项目到远程服务器上

    最近公司弄一个项目,jenkins在本地服务器,需要打包发布到远程的阿里云服务器上,弄了好一阵子. 这里记录下中间的几个坑. 这个Remote DIrectory 很重要,到时候时候会拷贝到这个目录下 ...

  7. Scrapy爬虫Demo 爬取资讯分类

    爬取新浪网导航页所有下所有大类.小类.小类里的子链接,以及子链接页面的新闻内容. 效果演示图: items.py import scrapy import sys reload(sys) sys.se ...

  8. C# 取得某月的最后一天和第一天

    strDate="2019-03" DateTime Date = DateTime.Parse(strDate); //要取得月份的某一天第一天).Date.AddDays( - ...

  9. 由swap引出的局部变量,形参和指针的小问题

    1.第一种实现swap函数的方法是: swap(int a,int b) { Int c = a;a = b;b =c; } 这表面一看确实是实现了整数a,b的交换,当拿来用时发现,结果并不是我们想要 ...

  10. 使用evenlet包实现 concurrent.futures.executor包的鸭子类

    适配成同一个同样的公有方法. # -*- coding: utf-8 -*- # @Author : ydf # @Time : 2019/7/3 10:35 import time import w ...