大佬們的總結文章:
第一篇(偏底層)
第二篇(偏理解)
第三篇(代碼解釋)
(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
;或者做爲觸發器,實現輕量級同步。