并发编程(二):全视角解析volatile
一、目录
1、引入话题-发散思考
2、volatile深度解析
3、解决volatile原子性问题
4、volatile应用场景
二、引入话题-发散思考
public class T1 {
/*volatile*/ boolean running=true;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m(){
System.out.println(Thread.currentThread().getName()</span>+":start!"<span style="color: #000000;">);
</span><span style="color: #0000ff;">while</span><span style="color: #000000;">(running){
</span><span style="color: #008000;">/*</span><span style="color: #008000;">try {
TimeUnit.MINUTES.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}</span><span style="color: #008000;">*/</span><span style="color: #000000;">
}
System.out.println(Thread.currentThread().getName()</span>+":end!"<span style="color: #000000;">);
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
T1 t</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> T1();
</span><span style="color: #0000ff;">new</span> Thread(()->t.m(),"t"<span style="color: #000000;">).start();
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e) {
e.printStackTrace();
}
t.running</span>=<span style="color: #0000ff;">false</span><span style="color: #000000;">;
}
}
运行结果:
无volatile:
t:start!
有volatile:
t:start!
t:end!


- 根据上述java内存模型可知,最开始running=true在主存中,开启线程A,线程会把主存的running=true复制一份写入工作内存的共享变量副本中。
- 当我们改变running=false,在主存中已经发生改变。
- 线程A一直在工作状态,没有空闲时间去知道主存的情况,而是一直在读本地内存的共享变量副本,也就一直running=true,取而代之也会产生上述情况。
三、volatile深度解析
- 根据上述java内存模型可知,最开始running=true在主存中,开启线程A,线程会把主存的running=true复制一份写入工作内存的共享变量副本中。
- 当我们改变running=false,在主存已经发生改变。
- 就在这时,当主存与工作内存发生不一致的时候,工作内存的共享变量会失效,那么工作内存就会去主存刷新一遍共享变量,所以running=false,自然就执行下面的代码啦!
int a=1;
int b =3;
int c=a*b;
//线程1:
context = loadContext(); //语句1
inited = true; //语句2 //线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
- 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
- 锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作
- volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
- 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
- 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作
- 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
- 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
- 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始
四、解决volatile原子性问题
1、volatile能解决原子性问题吗?什么是原子性呢,本不想解释,为了读者能够更透彻理解,再解释一下。
public class T2 {
volatile int count=0;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m(){
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<1000;i++<span style="color: #000000;">)
count</span>++<span style="color: #000000;">;
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
T2 t</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> T2();
List</span><Thread> threads=<span style="color: #0000ff;">new</span> ArrayList<Thread><span style="color: #000000;">();
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){
threads.add(</span><span style="color: #0000ff;">new</span> Thread(()->t.m(),"thread-"+<span style="color: #000000;">i));
}
threads.forEach((o)</span>-><span style="color: #000000;">o.start());
</span><span style="color: #008000;">//</span><span style="color: #008000;">等待所有线程都执行完</span>
threads.forEach((o)-><span style="color: #000000;">o.yield());
System.out.println(</span>"count:"+<span style="color: #000000;">t.count);
}
}
运行结果:
count:8710 //每次都不一样。
2、为什么加了volatile还是不能得到预期结果呢?因为它只保证了可见性,不能保证原子性。what?
再回忆java内存模型:

3、那怎么解决呢?
方式一:synchronized,jvm对synchronized进行了很大的优化,所以效率也没有想象中那么低。
public class T3 {
int count=0;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m(){
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<1000;i++<span style="color: #000000;">)
count</span>++<span style="color: #000000;">;
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
T3 t</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> T3();
List</span><Thread> threads=<span style="color: #0000ff;">new</span> ArrayList<Thread><span style="color: #000000;">();
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){
threads.add(</span><span style="color: #0000ff;">new</span> Thread(()->t.m(),"thread-"+<span style="color: #000000;">i));
}
threads.forEach((o)</span>-><span style="color: #000000;">o.start());
</span><span style="color: #008000;">//</span><span style="color: #008000;">等待所有线程都执行完</span>
threads.forEach((o)-><span style="color: #000000;">o.yield());
System.out.println(</span>"count:"+<span style="color: #000000;">t.count);
}
}
方式二:ReentrantLock,跟synchronized的作用差不多。
public class T5 {
ReentrantLock lock=new ReentrantLock();
int count=0;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m(){
lock.lock();
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<1000;i++<span style="color: #000000;">)
count</span>++<span style="color: #000000;">;
lock.unlock();
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
T4 t</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> T4();
List</span><Thread> threads=<span style="color: #0000ff;">new</span> ArrayList<Thread><span style="color: #000000;">();
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){
threads.add(</span><span style="color: #0000ff;">new</span> Thread(()->t.m(),"thread-"+<span style="color: #000000;">i));
}
threads.forEach((o)</span>-><span style="color: #000000;">o.start());
</span><span style="color: #008000;">//</span><span style="color: #008000;">等待所有线程都执行完</span>
threads.forEach((o)-><span style="color: #000000;">o.yield());
System.out.println(</span>"count:"+<span style="color: #000000;">t.count);
}
}
public class T4 {
AtomicInteger count=new AtomicInteger(0);
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m(){
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<1000;i++<span style="color: #000000;">)
count.getAndIncrement();
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
T4 t</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> T4();
List</span><Thread> threads=<span style="color: #0000ff;">new</span> ArrayList<Thread><span style="color: #000000;">();
</span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){
threads.add(</span><span style="color: #0000ff;">new</span> Thread(()->t.m(),"thread-"+<span style="color: #000000;">i));
}
threads.forEach((o)</span>-><span style="color: #000000;">o.start());
</span><span style="color: #008000;">//</span><span style="color: #008000;">等待所有线程都执行完</span>
threads.forEach((o)-><span style="color: #000000;">o.yield());
System.out.println(</span>"count:"+<span style="color: #000000;">t.count);
}
}
五、volatile应用场景
volatile boolean inited = false;
//线程1:
context = loadContext();
inited = true; //线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
九、版权声明
作者:邱勇Aaron
出处:http://www.cnblogs.com/qiuyong/
您的支持是对博主深入思考总结的最大鼓励。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,尊重作者的劳动成果。
参考:深入理解JVM、马士兵并发编程、并发编程实践
volatile关键字解析:http://www.importnew.com/18126.html
并发编程(二):全视角解析volatile的更多相关文章
- 并发编程(三):全视角解析volatile
一.目录 1.引入话题-发散思考 2.volatile深度解析 3.解决volatile原子性问题 4.volatile应用场景 二.引入话题-发散思考 public class T1 { /*vol ...
- Java并发编程中的设计模式解析(二)一个单例的七种写法
Java单例模式是最常见的设计模式之一,广泛应用于各种框架.中间件和应用开发中.单例模式实现起来比较简单,基本是每个Java工程师都能信手拈来的,本文将结合多线程.类的加载等知识,系统地介绍一下单例模 ...
- Java并发编程二三事
Java并发编程二三事 转自我的Github 近日重新翻了一下<Java Concurrency in Practice>故以此文记之. 我觉得Java的并发可以从下面三个点去理解: * ...
- [Java并发编程(五)] Java volatile 的实现原理
[Java并发编程(五)] Java volatile 的实现原理 简介 在多线程并发编程中 synchronized 和 volatile 都扮演着重要的角色,volatile 是轻量级的 sync ...
- [Java并发编程(四)] Java volatile 的理论实践
[Java并发编程(四)] Java volatile 的理论实践 摘要 Java 语言中的 volatile 变量可以被看作是一种 "程度较轻的 synchronized":与 ...
- [Java并发编程(三)] Java volatile 关键字介绍
[Java并发编程(三)] Java volatile 关键字介绍 摘要 Java volatile 关键字是用来标记 Java 变量,并表示变量 "存储于主内存中" .更准确的说 ...
- JAVA并发编程:相关概念及VOLATILE关键字解析
一.内存模型的相关概念 由于计算机在执行程序时都是在CPU中运行,临时数据存在主存即物理内存,数据的读取和写入都要和内存交互,CPU的运行速度远远快于内存,会大大降低程序执行的速度,于是就有了高速缓存 ...
- 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类
这篇说说java.util.concurrent.atomic包里的类,总共12个.网上有非常多文章解析这几个类.这里挑些重点说说. watermark/2/text/aHR0cDovL2Jsb2cu ...
- Python并发编程二(多线程、协程、IO模型)
1.python并发编程之多线程(理论) 1.1线程概念 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程(流水线的工作需要电源,电源就相当于 ...
随机推荐
- Rime中州韵导入QQ五笔词库
过程记录如下: 1.在QQ五笔中导出QQ五笔系统词库 2.使用「深蓝词库转换」转换QQ五笔系统词库,输入源修改为”五笔86版“,输出方式修改为Rime中州韵-五笔. 3.在Ubuntu中打开Termi ...
- java关于图片处理修改图片大小
最近做了一个关于图片浏览的内容.因为图片都是一些证件的资料的扫描件所以比较大,对系统的影响也是非常之大的,有很大可能直接把系统干死.那么我是这么处理的,给大家分享一下.如果大家有好的方案的话一定要早点 ...
- iOS手势UIGestureRecognizer的使用及手势冲突的解决办法【转】
转自:iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用 关于手势的一篇很好的帖子,转载过来免得丢失.你可能最感兴趣的是手势间的互斥处理,那么就搜索 4.手势间的互斥处 ...
- decimal模块
简介 decimal意思为十进制,这个模块提供了十进制浮点运算支持. 常用方法 1.可以传递给Decimal整型或者字符串参数,但不能是浮点数据,因为浮点数据本身就不准确. 2.要从浮点数据转换为De ...
- 【windows核心编程】HideProcess
A Basic Windows DKOM Rootkit Pt 1 https://www.landhb.me/posts/v9eRa/a-basic-windows-dkom-rootkit-pt- ...
- 由time.tzname返回值引发的对str、bytes转换时编码问题实践
Windows 10家庭中文版,Python 3.6.4, 下午复习了一下time模块,熟悉一下其中的各种时间格式的转换:时间戳浮点数.struct_tm.字符串,还算顺利. 可是,测试其中的time ...
- css-通过hover实现鼠标进过背景色变化
通过hover实现鼠标进过背景色变化 <!DOCTYPE html> <html lang="en"> <head> <meta cha ...
- python基础-装饰器,生成器和迭代器
学习内容 1.装饰器 2.生成器 3.迭代器 4.软件目录结构规范 一:装饰器(decorator) 1.装饰器定义:本质就是函数,用来装饰其他函数,即为其他函数添加附加功能. 2.装饰器原则:1)不 ...
- libsvm的使用
参考:http://www.cnblogs.com/GuoJiaSheng/p/4480497.html http://www.cnblogs.com/tornadomeet/archive/2012 ...
- java 多重继承
接口不仅仅只是一种更纯粹形式的抽象类,它的目标比这更高,因为接口是根本没有任何具体实现的--也就是说,没有任何与接口相关的存储,因此也就无法阻止多个接口的组合, 在导出类中,不强制要求必须有一个抽象的 ...