java并发编程之volatile
Java语言规范第三版中对volatile的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。
了解volatile关键字之前需要先了解下Java内存模型,java内存模型抽象示意图如下:
Java内存模型

线程A和线程B之间若要通信的话, 必须经历下面两个步骤 (1)线程A和线程A本地内存中更新过的共享变量刷新到主存中去。 (2)线程B到主存中去读取线程A之前更新过的共享变量。
由此可见执行下面的语句:
int a = 100 线程必须现在自己的工作线程中对变量i所在的缓存进行赋值操作,然后再写入主存当中,而不是直接将数值100写入主存中。
特性
- 可见性 当一个共享变量被volatile修饰时,它会保证修改的值立即被更新到主存,所以对其他线程是可见的。当其他线程需要读取该值时,其他线程会去主存中读取新值。相反普通的共享变量不能保证可见性,因为普通共享变量被修改后并不会立即被写入主存,何时被写入主存也不确定。当其他线程去读取该值时,此时主存可能还是原来的旧值,这样就无法保证可见性。 
- 有序性 java内存模型中允许编译器和处理器对指令进行重排序,虽然重排序过程不会影响到 - 单线程执行的正确性,但是会影响到多线程并发执行的正确性。这时可以通过volatile来保证有序性,除了volatile,也可以通过synchronized和Lock来保证有序性。synchronized和Lock保证每个时刻只有一个线程执行同步代码,这相当于让线程顺序执行同步代码,从而保证了有序性。如果不考虑原子性操作的话volatile比synchronized和Lock更轻量级,成本更低。
- 不保障原子性 volatile关键字只能保证共享变量的可见性和有序性。如果volatile修饰并发线程中共享变量, 而该共享变量是非原子操作的话,并发中就会出现问题。比如下面代码: 
public class HelloVolatile{
    public volatile int mNumber = 0;
    public static void main(String []args){
        final HelloVolatile hello = new HelloVolatile();
        for(int i =0; i<10; i++){
            new Thread(){
                public void run(){
                    for(int j =0; j<1000; j++){
                        hello.mNumber ++;
                    }
                }
            }.start();
        }
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println("number:"+hello.mNumber);
    }
}
这段代码预期结果是10000,可是每次执行结果都有可能不一样。这是因为自增或自减都是非原子操作。
(1) 假如mNumber此时等于100,线程1进行自增操作。
(2)线程1先读取了mNumber的值100,然后它被堵塞了。
(3)这时候线程2读取mNumber的值100,然后进行了自增操作,并写入到主存中, 这时候主存中的值为101。
(4)这时候线程1继续执行,因为此前线程1已经读取到值100,然后进行自增操作101,然后将101写入到主存中。
可以看到两个线程分别对100进行了+1操作,预期主存中的nNumber = 102,实际mNumebr = 101; 这就是因为非原子操作造成的。
使用场景
(1)并发编程中不依赖于程序中任意其状态的状态标识。可以通过关键字volatile代替synchronized, 提高程序执行效率,并简化代码。
(2)单例模式的双重检查模式DCL
public class DclSingleton {
    private volatile static DclSingleton mInstance = null;
    public static DclSingleton getInstance(){
        if(mInstance==null){
            synchronized (DclSingleton.class){
                if(mInstance==null){
                    mInstance = new DclSingleton();
                }
            }java学习群669823128
        }
        return mInstance;
    }
}
原理浅析
将volatile修饰的变量转变成汇编代码,如下:
... lock addl $0x0,(%rsp)
通过查IA-32架构安全手册可知,Lock前缀指令在多核处理器会引发两件事。
1)将当前处理器缓存行的数据写回到系统内存。
2)这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。
解读 :
为了提高,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存后再进行操作,但操作完不知道何时再写回内存。如果对声明了volatile的变量进行写操作,JVM会向处理机发送一条Lock前缀指令,将这个变量所在的缓存行的数据写回到系统内存。
但是写会内存后,如果其他处理器缓存的值还是旧的,再执行计算操作就会出现问题。所以在多处理器下,为了保证各个处理器缓存是一致的,就会实现缓存一致性协议,如下图:

每个处理器通过嗅探在总线上传播的数据来检查自己缓存的数据是否过期了,当处理器发现自己的缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态。当处理器对这个数据进行操作的时候,就会重新从系统内存中把数据读到处理器缓存中。
java学习群669823128
java并发编程之volatile的更多相关文章
- Java并发编程之volatile关键字解析
		一内存模型的相关概念 二并发编程中的三个概念 三Java内存模型 四深入剖析volatile关键字 五使用volatile关键字的场景 volatile这个关键字可能很多朋友都听说过,或许也都用过.在 ... 
- Java并发编程之volatile关键字
		大概是因为项目.业务的原因,工作上几乎还没有使用过多线程相关的功能,相关知识差不多都忘了,所以最近补一下基础. volatile用来修饰共享变量,volatile变量具有 synchronized 的 ... 
- Java 并发编程之volatile关键字解析
		摘录 1. 计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入.由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执 ... 
- Java并发编程之volatile的应用
		在多线程的并发编程中synchronized和volatile都扮演着重要的角色.volatile是轻量级的synchronized,它在多处理器的开发中保证了共享变量的可见性,可见性的意思是当一个线 ... 
- Java并发编程之volatile变量
		volatile提供了弱同步机制,用来确保将变量更新通知到其它线程.volatile变量不会被缓存在寄存器中或者对其它处理器不可见的地方,因此在读取volatile变量时总会返回最新写入的值.可以想象 ... 
- Java并发编程之CAS第一篇-什么是CAS
		Java并发编程之CAS第一篇-什么是CAS 通过前面几篇的学习,我们对并发编程两个高频知识点了解了其中的一个—volatitl.从这一篇文章开始,我们将要学习另一个知识点—CAS.本篇是<凯哥 ... 
- Java并发编程之CAS
		CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ... 
- Java并发编程之CAS二源码追根溯源
		Java并发编程之CAS二源码追根溯源 在上一篇文章中,我们知道了什么是CAS以及CAS的执行流程,在本篇文章中,我们将跟着源码一步一步的查看CAS最底层实现原理. 本篇是<凯哥(凯哥Java: ... 
- Java并发编程之CAS第三篇-CAS的缺点及解决办法
		Java并发编程之CAS第三篇-CAS的缺点 通过前两篇的文章介绍,我们知道了CAS是什么以及查看源码了解CAS原理.那么在多线程并发环境中,的缺点是什么呢?这篇文章我们就来讨论讨论 本篇是<凯 ... 
随机推荐
- JDBC连接数据库(一)
			原文地址http://www.cnblogs.com/hongten/archive/2011/03/29/1998311.html JDBC连接数据库 创建一个以JDBC连接数据库的程序,包含7个步 ... 
- javascript-复制
			cefCopy:function(str){ var t=this; if(!str || str=="暂无激活码") { $.notice("暂无激活码", ... 
- java操作Word总结
			import com.jacob.activeX.ActiveXComponent; import com.jacob.com.Dispatch; import com.jacob.com.Varia ... 
- 在Mybatis中使用连表查询的一次实际应用
			以前在工作中很少使用多表关联查询,对连表查询的具体作用和使用场景也没有很直观的认识,通过这次在项目中的实际应用,对此有了一定的认识,特记录如下. 关联表介绍: 分别是属性表attr_info.属性值表 ... 
- 2. Add Two Numbers(2个链表相加)
			You are given two non-empty linked lists representing two non-negative integers. The digits are stor ... 
- 2017-2018-1 信息安全技术 实验二 20155201——Windows口令破解
			2017-2018-1 信息安全技术 实验二 20155201--Windows口令破解 一.实验原理 口令破解方法 口令破解主要有两种方法:字典破解和暴力破解. 字典破解是指通过破解者对管理员的了解 ... 
- 20145311实验二 "Java面向对象程序设计"
			20145311实验二 "Java面向对象程序设计" 程序设计过程 实验内容 使用单元测试.TDD的方式设计实现复数类 Complex 编写代码: 1.首先设计实现复数类 Comp ... 
- 20145319 《网络渗透》MS12_020安全漏洞
			20145319 <网络渗透>MS12_020安全漏洞 一 实验内容 初步掌握平台matesploit辅助模块aux的使用 辅助模块包括扫描等众多辅助功能 本次展示DOS攻击的实现 有了初 ... 
- Git命令速查表【转】
			本文转载自:http://www.cnblogs.com/kenshinobiy/p/4543976.html 一. Git 常用命令速查 git branch 查看本地所有分支git status ... 
- 《EMCAScript6入门》读书笔记——14.Promise对象
