前言:多線程內存的劃分分爲:java線程、工作內存、save和load操作、主內存
volatile內部原理
volatile變量賦值後,彙編代碼會多一個lock操作,相當於內存屏障,作用是使本CPU的緩存寫入內存(相當於store和write操作),該寫入動作會使其他cpu的緩存無效,所以valatile變量的修改對其它cpu立即可見。
volatile的變量新增的內存語義
1、工作內存中,每次使用使用對象前必須先從主內存刷新最新的值。(其它cpu能看到變量最新的值)
2、工作內存中,每次修改變量後必須立刻同步會主內存中。(其它線程可以看到本線程對變量的修改)
3、volatile修飾的變量不會被指令從排序優化。(a肯定在b之前執行)
volatile boolean a;
int i=3;
int j=5;
volatile boolean b;
volatile作用
1、只能保證變量對所有線程的可見性,不能保證原子性和有序性。
2、每次使用前,都要先刷新,執行引擎看不到不一致的情況,因此可以認爲不存在一致性的問題。
3、使用Volatile變量會禁止指令重排序優化,其實就是內存屏障的作用,(volatile前的代碼不會在volatile變量之後執行,volatile變量之後的代碼也不會在volatile變量之前執行),保證代碼的執行順序與程序的順序相同。
4、每次使用變量前必須從主內存刷新最新的值,每次修改變量後必須立刻同步回主內存中。
5、目前商用虛擬機幾乎都把64位的數據讀寫操作作爲原子操作來對待。
JMM有8種原子的內存語義操作
1、lock(鎖定):作用於主內存的變量,它把一個變量標識爲一條線程獨佔的狀態。
2、unlock(解鎖):作用於主內存的變量,它吧一個處於鎖定狀態的變量釋放出來,釋放後的變量才能被其它線程鎖定。
3、read(讀取):作用於主內存的變量,它把一個變量的值從主內存傳輸到線程的工作內存中,以便隨後的load作用。
4、load(載入):作用於工作內存的變量,它把read操作從主內存中得到的變量值放入到工作內存的變量副本中。
5、assign(賦值):作用於工作內存的變量,它把一個從執行引擎接收到的值賦給工作內存的變量。
6、store(存儲):作用於工作內存的變量,把工作內存的變量傳到主內存中,以便隨後write操作。
7、write(寫入):作用於主內存的變量,把store操作從工作內存中的變量放到主內存中。
JMM的8中基本操作必須滿足的規則
1、不允許read和load、store和write操作之一單獨出現
2、發生過assign操作,必須把變量在工作內存改變後必須同步會主內存(同步的時間點不一定)
3、沒有發生過assign操作,不允許把數據從工作內存同步回主內存
4、新變量只能從主內存誕生
5、一個變量同一時刻只允許一個線程對其進行lock操作,但可被同一線程多次lock,lock幾次就要unlock幾次(synchronized同步代碼塊原理實現方式)
6、一個變量執行lock操作會清空工作內存中該變量的值
7、變量沒有執行lock操作則不能執行unlock操作
8、對變量執行unlock前,必須把此變量同步回主內存(synchronized同步塊可見性實現原理)
原子性
1、基本數據類型的訪問讀寫是具備原子性的
2、lock和unlock操作沒有開放個用戶直接使用,但是提供了更高級別的字節碼指令monitorenter和monitorexit,也就是java中的synchronized快
可見性
1、synchronized和final關鍵字也具有可見性
2、synchronized可見性是因爲,對一個變量執行unlock前,必須先把變量同步回主內存中(執行store和write操作)這條獲得的。
3、final可見性,被final修飾的字段在構造器中一旦初始化完成,其它線程就能看見該字段的值。
有序性
1、synchronized的有序性是通過 一個變量在同一時刻只允許一個線程對其進行lock操作 獲得的。
先行先發原則的作用
判斷數據是否存在競爭,線程是否安全的依據。
JMM中的順序性不能僅僅依靠volatile和synchronized.
先行發生原則和時間的先後順序沒什麼關係
什麼是先行先發原則
A操作先行發生於B操作,那A操作的影響能被B操作觀察到。(影響包括內存共享變量值的改變、發送消息、調用方法等)
1、程序次序:一個線程內,按照程序代碼的順序,寫在前面的操作先行發生於後面的操作。
2、synchronized相關:unlock先於後面的lock
一個unlock操作先行發生於後面對同一個鎖的lock操作。(後面只時間的先後)
3、volatile相關:寫先於讀
對volatile的寫操作先行發生於後面對這個變量的讀操作。(後面只時間的先後)
線程相關
4、start():Thread的start()方法先行發生於此線程的每一個動作。(start()先於所有動作)
5、join()或isAlive():線程中的所有操作先行發生於對此線程的終止檢驗(所有動作先於Thread.join方法結束、Thread.isAlive等)
6、interrupt():線程中斷先行發生於對中斷檢驗代碼(interrupt()先於Thread.interrupted())
7、finalize():初始化方法的完成先於finalize()的開始
8、傳遞性:A操作先於B操作,B操作先於C操作,那A操作先於C操作。