深刻理解volatile关键字

关于volatile关键字的话,工做中没有遇到使用场景,大部分是面试的时候必问的一个题目;
本文主要内容摘自《JAVA高并发编程详解》java

1. CPU Cache模型

CPU Cache主要是为了解决CPU与内存之间访问速度的差别问题,Cache则是在程序运行的过程当中会将运算的所需数据从主内存复制一份到CPU Cache中,这样CPU在计算的过程当中,能够直接从CPU Cache中读取或写入,当运算结束以后,CPU Cache再将最新数据刷新到主内存当中,CPU直接经过访问Cache的方式替代直接访问主存的方式,极大地提升了CPU的吞吐计算能力;面试

2.CPU解决缓存一致性问题

CPU Cache虽然解决了CPU与主存访问速度差别,但也引来了另一个问题,就是缓存的一致性问题;
好比i++的整个计算过程是这样的:编程

  1. 读取主存i的值到CPU Cache中;
  2. 对i进行加一操做;
  3. 将结果写回到CPU Cache中;
  4. 将数据刷新到主内存中;

在单线程的状况下,这个计算过程没有任何问题,可是在多线程状况下,每一个线程都会将i的值加载到本身的本地内存;
好比如今主内存有i=0,线程A与线程B将变量i加载到了本身的工做内存,此时线程A对i加1操做,写回主内存,可是在这时候,线程B感知不到线程A的更改,因此线程B对i加一后,刷新到主内存后,主内存的i仍是1;
解决思路:
当CPU在操做Cache中的数据时,若是发现该变量是一个共享变量,也就是说在其余的CPU Cache中也存在一个副本,那么进行以下操做: 缓存

  1. 读取操做:不作任何处理,只是将Cache中的数据读取到寄存器;
  2. 写入操做:发出信号通知其余CPU将该变量在Cache中的状态设置为无效,其余CPU在进行该变量读取的时候不得不到主内存中再次获取;

3.java内存模型

Java的内存模型决定了一个线程对共享变量的写入什么时候对其余线程可见,Java内存模型定义了线程和主内存之间的抽象关系:多线程

  • 共享变量存储于主内存之中,每一个线程均可以访问;
  • 每一个线程都有私有的工做内存或者称为本地内存;
  • 工做内存只存储该线程对共享变量的副本;
  • 线程不能直接操做主内存,只有先操做了工做内存以后才能写入主内存;
    深刻理解volatile关键字

    假设主内存的共享变量x=0,线程A与线程B分别拥有共享变量x的副本,线程A对x作加1操做后,将x的值刷新到主内存中,当线程B想要使用副本x的时候,就会发现变量已经失效了,必须到主内存中再次获取而后存入本身的工做内存中;并发

4.JMM如何保证并发三大特性

JMM是指 Java Memory Mode指定了Java虚拟机如何与计算机的主内存进行工做;ide

1) 并发编程的三大特性

(1) 原子性
原子性是指在一次的操做或者屡次操做中,要么全部的操做所有都获得了执行而且不会受到任何因素的干扰而中断,要么全部的操做都不执行;高并发

(2) 可见性
可见性是指,当一个线程对共享变量进行了修改,那么另外的线程能够当即看到修改后的最新值;优化

(3) 有序性
所谓有序性是指程序代码在执行过程当中的前后顺序,好比java编译器在优化的过程当中会发生指令重排序;线程

2) JMM保证三大特性

(1) JMM与有序性
对基本数据类型的变量读取赋值操做都是原子性的,对引用类型的变量的读取与赋值的操做也是原子性的;
可是,若是是i++这类的操做,它在底层其它是进行了两步操做的,加一和赋值;
(2) JMM与可见性
java提供了如下三种方式来保证可见性:

  • 使用validate关键字,对于共享变量的写入首先写入到工做内存,可是修改结束后会马上将其刷新到主内存;
  • 经过加锁的方式实现可见性:不管是synchronized或者Lock,同一时刻只有一个线程得到锁,而且还会确保在锁释放以前,会将对变量的修改刷新到主内存;

(3) JMM与有序性
volatile对一个变量的写操做要早于对这个变量以后的读操做;

5.总结

1.能够保证不一样线程之间对共享变量操做时的可见性;2.能够防止指令重排序;3.volatile不能保证原子性;

相关文章
相关标签/搜索