volatile-最轻量级的并发实现及其内存语义

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

一、volatile定义

volatile是java并发编程中修饰类的成员变量、成员属性或者对象的一个关键字。是java并发编程中最轻量级的

并发实现,保证所修饰的变量对多个线程内存可见。在一个线程写,多个线程读的场景下,首选使用volatile关键字。

二、使用案例

此处用一个线程安全的单例模式来说明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 ;

三、深入剖析

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

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

(1)、每个线程都会有自己的本地缓存,如上图所示,线程A,线程B都会有自己的本地缓存,用来缓存线
程本身所需要使用的数据。
(2)、正常情况下,当线程A改变了自己本地缓存中的a变量的值,并不会马上刷新到主内存,同理,线程
B也一样。
(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的并发实现是利用cpu的lock前缀指令对消息总线进行加锁,同时让volatile
当前所在线程的本地缓存内容强行刷进主内存,并让其他使用了该变量的本地缓存失效,当其他线程需要
使用volatile所修饰的变量的时候就会重新到主内存中读取。
本文为博主原创文章,欢迎留言交流,转载请注明原文出处。java开发工具下载地址及安装教程大全,点这里。更多技术文章,在这里

原文地址:https://www.cnblogs.com/darendu/p/10947873.html

时间: 2024-10-13 17:35:55

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

并发编程-Java内存模型到底是什么

内存模型 在计算机CPU,内存,IO三者之间速度差异,为了提高系统性能,对这三者速度进行平衡. CPU 增加了缓存,以均衡与内存的速度差异: 操作系统增加了进程.线程,以分时复用 CPU,进而均衡 CPU 与 I/O 设备的速度差异: 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用. 以上三种系统优化,对于硬件的效率有了显著的提升,但是他们同时也带来了可见性,原子性以及顺序性等问题.基于Cpu高速缓存的存储交互很好得解决了CPU和内存得速度矛盾,但是也提高了计算机系统得复杂度,引入了新

【死磕Java并发】-----Java内存模型之分析volatile

前篇博客[死磕Java并发]-–深入分析volatile的实现原理 中已经阐述了volatile的特性了: volatile可见性:对一个volatile的读,总可以看到对这个变量最终的写: volatile原子性:volatile对单个读/写具有原子性(32位Long.Double),但是复合操作除外,例如i++; JVM底层采用"内存屏障"来实现volatile语义 下面LZ就通过happens-before原则和volatile的内存语义两个方向介绍volatile. volat

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

趁周末,把以前的书拿出来,再翻一番,顺便做个笔记: 内存屏障:用来控制和规范cpu对内存操作的顺序的cpu指令. 内存屏障列表: 1.loadload:确保"前者数据装载"先于"后者装载指令": 2.storestore:确保"前者数据"先于"后者数据"刷入系统内存,且,"前者刷入系统内存的数据"对"后者是可见的": 3.loadstore:确保"前者装载数据"先于&

并发编程-锁相关的内存语义

锁的内存语义本质上可以说是对共享变量的更新,能及时让其他线程观察到:并且通过内存屏障,组织编译器或处理器指令重排序,导致多线程下不一致的现象. 1. volatile内存语义 见上一篇文章. 2. 锁的内存语义 (1)锁的释放和获取的内存语义 当线程释放锁时,JMM会将本地内存中的共享变量同步到主内存中: 当线程获取锁时,JMM会将该线程对应的本地内存置为无效,从而使得被监视器保护的临界区代码必须从主内存中读取共享变量. (2)锁内存语义的实现 ReentrantLock的实现依赖于Abstra

Java内存模型-volatile的内存语义

一 引言 听说在Java 5之前volatile关键字备受争议,所以本文也不讨论1.5版本之前的volatile.本文主要针对1.5后即JSR-133针对volatile做了强化后的了解. 二 volatile的特性 开门见山,volatile变量自身具有以下特性: 可见性(最重要的特性).对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入. 原子性.对任意(包括64位long类型和double类型)单个volatile变量的读/写具有原子性.但是类型于a

java并发学习--第十章 java内存模型的内存语义

一.锁的内存语义 所为的java内存模型的内存语义指的就是在JVM中的实现原则. 锁的内存语义:锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 我们把上面这句话再整理下: 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中. 当线程获取锁时,JMM会把该线程对应的本地内存置为无效.从而使得被监视器保护的临界区代码必须要从主内存中去读取共享变量. 锁的内存语义实现: synchronized.ReentrantLock: 二.volatile内存

volatile写读的内存语义

1,当写一个volatile变量时,JMM(java内存模型)会把该线程本地内存中的所有共享变量刷新到主内存中去 2,当读取一个volatile变量时,该线程会将本地内存置为无效,线程将从主内存中读取共享变量. 总结,volatile变量可以实现线程之间的通信. 当对一个volatile变量写操作时,实际上就是指明我对主内存中的共享变量产生更改,接下来读取这个volatile变量的线程就会从主线程中取回最新的共享变量值.

java多线程03-----------------volatile内存语义

java多线程02-----------------volatile内存语义 volatile关键字是java虚拟机提供的最轻量级额的同步机制.由于volatile关键字与java内存模型相关,因此,我们在介绍volatile关键字之前,对java内存模型进行更多的补充(之前的博文也曾介绍过). 1. java内存模型(JMM) JMM是一种规范,主要用于定义共享变量的访问规则,目的是解决多个线程本地内存与共享内存的数据不一致.编译器处理器的指令重排序造成的各种线程安全问题,以保障多线程编程的原

锁的内存语义

锁是Java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 锁的获取和释放 线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中. 线程获取锁时,JMM会把该线程对应的本地内存置为无效.从而使得被监视器保护的临界区代码必须从主内存中读取共享变量. 在共享变量的可见性方面,锁的释放和获取和volatile是类似的. 对volatile变量进行写操作,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中. 对volati