并发编程实战第三章(二)
来源:互联网 发布:光大证券mac版 编辑:程序博客网 时间:2024/06/05 11:18
Volatile关键字解读及案例
在上篇文中指出Volatile的轻量、读取拿到的是最新写入值、严格的使用条件、只能保证可见性。本篇中主要加深对其理解和使用demo。
1、Java中的内存模型
在理解之前,要能先了解Java的内存模型。明确在内存模型中有严格的区分:主内存和线程内存。主内存是线程间共享的,比如堆内存。线程内存是指每个线程都会有工作内存,工作内存是不能共享,即其他线程不可以访问的,且对于数据操作只能在工作内存中完成。工作内存中的数据是从主内存中拷贝而来的,线程间数据交换必须通过主内存来完成。内存模型如下图所示:
在上述的内存模型中,会出现脏数据的问题。
线程对主内存数据的修改过程,总结:线程内存对主内存的数据更新不是原子操作。(这一点必须明确,因为这是为什么Volatile不能保证原子性的原因)。可分为以下步骤:
1、从主内存中拷贝到线程的工作内存——读取
2、线程读取后的状态具有不确定性,可能处于阻塞状态或立刻对工作内存中进行修改并写入主内存
2、可见性、原子性、有序性
先来了解Java并发编程中的三个基本的概念:可见性、原子性、有序性
可见性:这里是指,线程工作内存对主内存中的数据进行修改,并将修改的写入到主内存中,其他线程能否及时看到主内存中数据已经有变化。
原子性:对于单个或多个操作,要么都成功,要么都失败。
有序性:在JVM中在满足数据依赖的基础上,编译器、处理器允许指令重排序,即不按照代码由上到下顺序执行。
在Java中的有序性8个原则是:
①程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
②锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作
③volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
④传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
⑤线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作
⑥线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
⑦线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
⑧对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始
这8条规则中,前4条规则是比较重要的,后4条规则都是显而易见的。
3、volatile关键字
当一个共享变量被声明为Volatile变量后,应当明确改变具备的特性是:
1、一旦有一个线程对共享变量的值修改了并完成对主内存的写入,那么其他缓存该变量的线程都会被迫更新为主内存的值。
2、不能保证原子性。(本质原因是线程内存与主内存之间的数据传递不是原子操作,共有8个原子操作)
示例代码:
/** * 验证Volatile只能保证可见性,不能确保原子性 * * @author caiqiang * @version 2017年4月10日 * @see VolatileTest * @since */public class VolatileTest { // Volatile关键字修饰,保证可见性 public volatile int inc = 0; // 自增,对于自增i++不是原子操作 public synchronized void increase() { inc++ ; } public static void main(String[] args) { final VolatileTest test = new VolatileTest(); // 该线程循环执行10次 for (int i = 0; i < 10; i++ ) { new Thread() { // 线程1000次自增 public void run() { for (int j = 0; j < 1000; j++ ) test.increase(); }; }.start(); } while (Thread.activeCount() > 1) // 保证前面的线程都执行完 Thread.yield(); System.out.println(test.inc); }}从开发者的角度来看,这个结果应该是10000。此实验是验证Volatile是否能保证原子性,如果实验的结果是10000,则Volatile能保证原子性;如果实验结果是小于10000,则Volatile不能保证原子性。实验的结果几乎都是小于10000(在有限的次数内)。
4、volatile的典型应用:
1、状态标记量
volatile boolean flag = false; //线程1while(!flag){ doSomething();} //线程2public void setFlag() { flag = true;}该例如果没有volatile会怎样呢?线程1正在执行循环,线程2修改了共享变量flag,但是没有立即写入主内存中,那么会导致线程1还在执行循环。这也就是说,加上volatile关键字会使线程在工作内存的修改会立即写入主内存中。
2、单例模式
class Singleton{ private volatile static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if(instance==null) { synchronized (Singleton.class) { if(instance==null) instance = new Singleton(); } } return instance; }}参考文章:http://mp.weixin.qq.com/s/JY1totcwH0E-nboQ_I01Rw
- 并发编程实战第三章(二)
- 《并发编程实战》第三章
- Java并发编程实战第三章学习
- Java并发编程实战(学习笔记二 第三章 对象的共享 上)
- Java并发编程实战(学习笔记二 第三章 对象的共享 下 线程封闭)
- Java并发编程实战~笔记~章二
- Java并发编程实战笔记(二)
- java并发编程实战(二)
- 《Java并发编程实战》第三章 对象的共享 读书笔记
- java并发编程实战手册第三章---CountDownLatch使用
- java并发编程实战阅读笔记(第三章)对象的共享
- Java并发编程实战-章二~volatile的思考
- 学习java并发编程实战的一些心得体会(二)
- 《java并发编程实战》基础构建模块(二)
- Java并发编程实战学习笔记(二)-可见性
- java并发编程实战笔记(二)任务取消
- java并发编程实战(二)—线程安全
- Java并发编程实战(二)基础构建模块
- Struts 02
- JVM运行时数据区
- 支付宝等第三方支付原理与概述
- JDBC基本知识
- 数据库的增删改查crud(sql语句)
- 并发编程实战第三章(二)
- PlusOne
- Activity回传数据(装备选择)
- unity 使用角色控制器CharacterController移动对象造成模型动画的抖动
- archlinux配置网络桥
- SCOI2017 真滚粗记
- 算法设计与应用基础-第七周
- 13 Fibonacci数
- 博弈论-巴什博弈-斐波拉契数-尼姆游戏-拍卖土地-威佐夫博弈入门理解Java