一、认识volatile关键字

程序举例

用一个线程读数据,一个线程改数据

  1. public class ReaderAndUpdater {
  2. public static int MAX = 50;
  3. static volatile int initValue = 0;
  4. public static void main(String[] args) {
  5. new Thread(()->{
  6. int localValue = initValue;
  7. while (localValue < MAX){
  8. if(localValue!=initValue){
  9. System.out.println("Reader:"+initValue);
  10. localValue=initValue;
  11. }
  12. }
  13. },"Reader").start();
  14. new Thread(()->{
  15. int localValue = initValue;
  16. while (localValue<MAX){
  17. System.out.println("Updater:"+(++localValue));
  18. initValue = localValue;
  19. try {
  20. TimeUnit.SECONDS.sleep(2);
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. },"Updater").start();
  26. }
  27. }

出现的问题,存在数据的不一致问题
volatile 关键字实现原理(三) - 图1

解决方法:加volatile关键字
volatile 关键字实现原理(三) - 图2

二、机器硬件CPU与JMM

参考文章:CPU Cache - 留一日白 - 博客园

1.Cpu Cache模型
volatile 关键字实现原理(三) - 图3

volatile 关键字实现原理(三) - 图4

2.cpu缓存的一致性问题
volatile 关键字实现原理(三) - 图5

解决方案
a. 总线加锁(粒度太大)
b. MESI

  • 1.读操作:不做任何事情,将内存中的数据读到Cache中。
  • 2.写操作,发出信号通知其它cpu将该变量的CacheLine置为无效,其它的cpu要访问这个变量的时候,只能从内存中获取。

c.Java内存模型
volatile 关键字实现原理(三) - 图6

  • 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.状态标志(开关模式)

  1. public class ShutDowsnDemmo extends Thread{
  2. private volatile boolean started=false;
  3. @Override
  4. public void run() {
  5. while(started){
  6. }
  7. }
  8. public void shutdown(){
  9. started=false;
  10. }
  11. }

2.双重检查锁定(double-checked-locking)

  1. public class Singleton {
  2. private volatile static Singleton instance;
  3. public static Singleton getInstance(){
  4. if(instance==null){
  5. synchronized (Singleton.class){
  6. if(instance ==null)
  7. instance=new Singleton();
  8. }
  9. }
  10. return instance;
  11. }
  12. }

3.需要利用顺序性。

五、volatile和synchronized的区别

1.使用上的区别

volatile只能修饰变量,synchronized只能修饰方法和语句块。

2.对原子性的保证

synchronized可以保证原子性,volatile不能保证原子性

3.对可见性的保证

都可以保证可见性,但实现原理不同

volatile对变量加了lock,synchronized使用monitorEnter和monitorExit

4.对有序性的保证

volatile能保证有序性,synchronized可以保证有序性,但是代价太大(重量级),并发退化到串行

5.其他

synchronized 引起阻塞
volatile 不会引起阻塞