一、认识volatile关键字
程序举例
用一个线程读数据,一个线程改数据
public class ReaderAndUpdater {public static int MAX = 50;static volatile int initValue = 0;public static void main(String[] args) {new Thread(()->{int localValue = initValue;while (localValue < MAX){if(localValue!=initValue){System.out.println("Reader:"+initValue);localValue=initValue;}}},"Reader").start();new Thread(()->{int localValue = initValue;while (localValue<MAX){System.out.println("Updater:"+(++localValue));initValue = localValue;try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}}},"Updater").start();}}
出现的问题,存在数据的不一致问题
解决方法:加volatile关键字
二、机器硬件CPU与JMM
1.Cpu Cache模型

2.cpu缓存的一致性问题
解决方案
a. 总线加锁(粒度太大)
b. MESI
- 1.读操作:不做任何事情,将内存中的数据读到Cache中。
- 2.写操作,发出信号通知其它cpu将该变量的CacheLine置为无效,其它的cpu要访问这个变量的时候,只能从内存中获取。
c.Java内存模型
- 1.主内存的数据所有线程都可以访问。
- 2.每个线程都有自己的本地内存。
- 3.本地内存的数据:局部变量、内存的副本
- 4.线程不能直接修改内存中的数据,只能读到本地内存来修改,修改完成后刷新到主内存。
三、volatile关键字的语义分析
volatile作用:让其它线程能够马上感知到某一线程对某个变量的修改。
1.保证可见性
对共享变量的修改,其它线程马上能感知到
不能保证原子性 读、写(i++)
2.保证有序性
重排序(编译阶段,指令优化阶段)
输入程序的代码顺序并不是实际代码执行的顺序
重排序后对单线程没有影响,对多线程有影响
volatile
Happens-before原则
volatile原则:
- 对于volatile修饰的变量:
- a.volatile之前的代码不能调整到它的后面
- b.volatile之后的代码不能调整到它的前面
- c.位置不变化
3.volatile的原理实现机制
轻量级锁
反编译
volatile int a;
LocK:a;
四、volatile的使用场景
1.状态标志(开关模式)
public class ShutDowsnDemmo extends Thread{private volatile boolean started=false;@Overridepublic void run() {while(started){}}public void shutdown(){started=false;}}
2.双重检查锁定(double-checked-locking)
public class Singleton {private volatile static Singleton instance;public static Singleton getInstance(){if(instance==null){synchronized (Singleton.class){if(instance ==null)instance=new Singleton();}}return instance;}}
3.需要利用顺序性。
五、volatile和synchronized的区别
1.使用上的区别
volatile只能修饰变量,synchronized只能修饰方法和语句块。
2.对原子性的保证
synchronized可以保证原子性,volatile不能保证原子性
3.对可见性的保证
都可以保证可见性,但实现原理不同
volatile对变量加了lock,synchronized使用monitorEnter和monitorExit
4.对有序性的保证
volatile能保证有序性,synchronized可以保证有序性,但是代价太大(重量级),并发退化到串行
5.其他
synchronized 引起阻塞
volatile 不会引起阻塞
