大佬们的总结文章:
第一篇(偏底层)
第二篇(偏理解)
第三篇(代码解释)
(PS:本文基于以上三篇文章)
首先java内存模型(JMM)
中,每一个线程有本身的工做内存,同时还有一个共享的主内存。以下图:
不直接让线程从主内存(MainMemory)
中读取数据,是由于cpu的指令速度远超内存的存取速度
,因此现代计算机系统都不得不加入一层读写速度尽量接近处理器运算速度的高速缓存(Cache)
来做为内存与处理器之间的缓冲。也就是图中的工做内存。
基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,可是也为计算机系统带来更高的复杂度,由于它引入了一个新的问题:缓存一致性(CacheCoherence)
。以下图:
假如说线程1修改了data变量的值为1,而后将这个修改写入本身的本地工做内存。那么此时,线程1的工做内存里的data值为1。然而,主内存里的data值仍是为0!线程2的工做内存里的data值也仍是0。html
这就是所谓的 java并发编程中的可见性问题:
多个线程并发读写一个共享变量的时候,有可能某个线程修改了变量的值,可是其余线程看不到!也就是对其余线程不可见!
要解决可见性问题,只须要用volatile修饰data。
以下代码:java
public class Solution { private static volatile int data = 0; public static void main(String[] args) { DataThread1 d1 = new DataThread1(); DataThread2 d2 = new DataThread2(); // 线程1读取和修改data变量值 d1.run(); // 线程2读取data变量值 d2.run(); } }
这里说一下volatile的主要功能:编程
小结:
对一个变量加了volatile关键字修饰以后,只要一个线程修改了这个变量的值,立马强制刷回主内存。
接着强制过时其余线程的本地工做内存中的缓存,最后其余线程读取变量值的时候,强制从新从主内存来加载最新的值!
这样就保证,任何一个线程修改了变量值,其余线程立马就能够看见了!这就是所谓的volatile保证了可见性的工做原理!缓存volatile主要做用是保证可见性以及有序性。
有序性涉及到较为复杂的指令重排
、内存屏障
等概念,本文没说起,可是volatile是不能保证原子性的!安全
- 也就是说,volatile主要解决的是一个线程修改变量值以后,其余线程立马能够读到最新的值,是解决这个问题的,也就是可见性!
- 可是若是是多个线程同时修改一个变量的值,那仍是可能出现多线程并发的安全问题,致使数据值修改错乱,volatile是不负责解决这个问题的,也就是不负责解决原子性问题!
- 原子性问题,得依赖synchronized、ReentrantLock等加锁机制来解决。
JMM主要就是围绕着如何在并发过程当中如何处理原子性、可见性和有序性这3个特征来创建的,经过解决这三个问题,能够解除缓存不一致的问题。而volatile跟可见性和有序性都有关。
(PS:三种特性看第三篇文章)多线程
状态量标记
int a = 0; volatile bool flag = false; public void write() { a = 2; //1 flag = true; //2 } public void multiply() { if (flag) { //3 int ret = a * a;//4 } }
单例模式中的应用
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; } }
boolean flag
;或者做为触发器,实现轻量级同步。