上一个文章讲过了Synchronized之后,现在来了解一下Volatile
1 认识Volatile关键字
程序举例
用一个线程读数据,一个线程改数据
存在数据的不一致性
2 机器硬件CPU与JMM
(1)CPU Chache模型
(2)CPU 缓存一致性问题
解决方案
1)总线加锁(粒度太大 效率较慢)
2)MESI协议
a. 读操作:不做任何处理,将Cache中的数据读到cpu寄存器
b. 写操作: 发出信号通知其他的cpu将该变量的Cache line置为无效,其他的cpu要访问这个变量时候,只能从内存中获取。
Cache line 是一个cpu的实现机制。cpu的cache中会增加很多的Cache line(理解为一条数据线)
(3)java内存模型
1)主存中的数据,所有线程都可以访问(共享数据)
2)每个线程都有自己的工作空间(本地内存,线程间不可见)
3)工作空间数据: 局部变量、内存副本
4)线程不能直接修改主内存中的数据,只能把数据读取到工作空间中进行修改,修改完成后,将数据刷新到主内存
3 Volatile关键字的语义分析
volatile作用:让其他线程能够马上感知到某一线程对某个变量的改动
(1)保证可见性 :对共享变量的修改其他的线程马上能感知到
不能保证原子性
(2)保证有序性
重排序(编译阶段、指令优化阶段)
输入程序的代码顺序,并不是实际执行的顺序。重排序后对单线程没有影响,但是对多线程有影响。
volatile规则:
对于volatile修饰的变量
(1)volatile之前的代码不能调整到他的后面
(2)volatile之后的代码不能调整到他的前面
(3)霸道位置不变化
(3)volatile的原理和实现机制
HSDIS --- 反编译--- 汇编
java --- class ---JVM --- ASM文件(汇编文件)
Volatile int a; ---> 在汇编语言里 会有一个Lock关键字。。 对Volatile的位置进行锁定
4 Volatile的使用场景
(1)状态标志开关
public class ShutDowsnDemmo extends Thread{
private volatile boolean started = false;
@Override
public void run() {
while (started){
//do work();
}
}
public void shutdown(){
started = false;
}
}
(2)双重检查锁定(DCL double-checked-locking)
public class Singleton {
private volatile static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
(3) 需要利用顺序性
5 Volatile和Synchronized的区别
(1)使用上的区别
Volatile只能修饰变量,Synchronized只能修饰方法、代码块。
(2)对原子性的保证
Synchronized可以保证原子性 Volatile不能保证原子性
(3)对可见性的保证
都可以保证可见性。但实现原理不同
Volatile对变量加上了LOCK。 Synchronized使用的monitorEnter和monitorexit.
(4)对有序性的保证
Volatile能保证了有序性。Synchronized可以保证有序性。但是代价是如果是中重量级锁的时候会有并发退化到串行。
(5)其他
Synchronized会引起阻塞,Volatile不会引起线程阻塞。