int a; boolean flag; public synchronized void init(){// ① a = 100; // ② flag = true; // ③ }// ④ public synchronized void doTask(){ // ⑤ if(flag){ // ⑥ int result = a; // ⑦ } } // ⑧
假設線程A執行init(),線程B執行doTask(),有以下的happens-before關係:java
根據傳遞規則,保證init()方法全部的修改對doTask()方法可見,happens-before關係以下所示
緩存
一、2間的表示程序次序性規則,四、5間的表示監視器規則,因爲三、4有happens-before關係,四、5有happens-before關係,因此根據傳遞性規則,二、6間有happens-before關係。數據結構
線程A釋放鎖以前全部可見的共享變量,在線程B獲取同個鎖以後就變得可見了。app
當釋放鎖時,JMM會把線程對應的本地內存中的共享變量刷新到主內存。以上面的例子爲例,共享數據的狀態示意圖以下所示
學習
當A線程獲取到鎖時,JMM會把該線程對應的本地內存置爲無效。從而使得被監視器保護的臨界區代碼必須從主內存中讀取共享變量。線程
對比鎖釋放-獲取的內存語義和volatile的寫-讀內存語義能夠看出:鎖釋放與volatile寫有相同的內存語義;鎖獲取和volatile讀有相同的內存語義。3d
這裏判斷語義是否相同是經過兩個操做的流程是否相同,好比線程A的鎖釋放完時,刷新至主內存;volatile寫完後,刷新至主內存,並通知其餘線程本地內存的共享變量失效(在鎖釋放環節裏是交給鎖獲取執行);code
synchronized是經過控制對象頭來控制鎖的升級,可是具體獲取鎖和釋放鎖的流程藏在JVM裏,這裏將經過ReentrantLock類比synchronized過程。對象
這裏要學習的是CAS,JDK文檔對該方法的說明以下:若是當前狀態值等於預期值,則以原子方式將同步設置爲給定的更新值。此操做具備volatile讀和寫的語義。
前面講到volatile寫保證volatile寫不會和前面的操做發生重排序,volatile讀保證volatile讀不會和後面的操做發生重排序。組合這兩個條件就意味着同時實現了 禁止某一操做和操做前、操做後的重排序。CAS操做就是如此,它在是經過加上lock前綴來實現如下的功能:
正是由於CAS同時具備volatile讀和寫的內存語義,所以Java線程之間的通訊有下面四種方式。
JUC包的通用化的實現模式:
AQS,非阻塞數據結構和原子變量類,這些JUC包中的基礎都是使用上面的模式來實現的,而JUC包的高層類又是依賴這些基礎類來實現的。從總體看,JUC包的實現示意圖以下所示。