鎖的內(nèi)存語(yǔ)義
鎖的釋放-獲取建立的happens-before關(guān)系
鎖是Java并發(fā)編程中最重要的同步機(jī)制.鎖除了讓臨界區(qū)互斥執(zhí)行外,還可以讓釋放鎖的線程向獲取同一個(gè)鎖的線程發(fā)送信息.

假設(shè)線程A執(zhí)行writer()方法,隨后線程B執(zhí)行reader()方法.根據(jù)happens-before規(guī)則,這個(gè)過程happens-before關(guān)系可以分為3類.
- 根據(jù)程序次序規(guī)則,1 happens-before 2,2 happens-before 3;4 happens-before 5,5 happensbefore 6。
- 根據(jù)監(jiān)視器鎖規(guī)則,3 happens-before 4
- 根據(jù)happens-before的傳遞性,2 happens-before 5
上述happens-before關(guān)系的圖形化表示形式如下圖所示

每一個(gè)箭頭鏈接的兩個(gè)節(jié)點(diǎn),代表了一個(gè)happens-before關(guān)系。黑色箭頭表示程序順序規(guī)則;橙色箭頭表示監(jiān)視器鎖規(guī)則;藍(lán)色箭頭表示組合這些規(guī)則后提供的happens-before保證。
上圖中在線程A釋放了鎖之后,隨后線程B獲取同一個(gè)鎖。在上圖職工2 happens-before 5,因此,線程A在釋放鎖之前所有可見的共享變量,在線程B獲取同一個(gè)鎖之后,將立刻變得對(duì)線程B可見。
鎖的釋放和獲取的內(nèi)存語(yǔ)義
當(dāng)線程釋放鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存的共享變量刷新到主內(nèi)存.以上面的MonitorExample程序?yàn)槔?,A線程釋放鎖后,共享數(shù)據(jù)的狀態(tài)示意圖如圖下圖所示。

當(dāng)線程獲取鎖時(shí),JMM會(huì)把該線程所對(duì)應(yīng)的本地內(nèi)存置為無(wú)效.從而使得被監(jiān)視器保護(hù)的臨界代碼必須從主內(nèi)存中讀取共享變量.

對(duì)比鎖釋放-獲取內(nèi)存語(yǔ)義與volatile寫-讀的內(nèi)存語(yǔ)義可以看出:鎖釋放與volatile寫有相同的內(nèi)存語(yǔ)義;鎖獲取與volatile讀有相同的內(nèi)存語(yǔ)義。
對(duì)鎖釋放和鎖獲取的內(nèi)存語(yǔ)義小結(jié)
- 線程A釋放一個(gè)鎖,實(shí)質(zhì)上是對(duì)線程A向接下來(lái)要獲取這個(gè)鎖的某個(gè)線程發(fā)出了(線程A對(duì)共享變量所做修改的)消息
- 線程B獲取一個(gè)鎖,實(shí)質(zhì)上是線程B接收之前某個(gè)線程發(fā)出的(在釋放這個(gè)鎖之前對(duì)共享變量所做修改的)消息
- 線程A釋放鎖,隨后線程B獲取這個(gè)鎖,這個(gè)過程實(shí)質(zhì)上是線程A通過主內(nèi)存向線程B發(fā)送消息(共享變量).
鎖內(nèi)存語(yǔ)義的實(shí)現(xiàn)
示例代碼

ReentrantLock的實(shí)現(xiàn)依賴于Java同步器框架AbstractQueuedSynchronizer(AQS)。AQS使用一個(gè)整型的volatile變量(命名為state)來(lái)維護(hù)同步狀態(tài)

ReentrantLock分為公平鎖和非公平鎖
公平鎖
使用公平鎖時(shí),加鎖方法lock()調(diào)用軌跡如下。
1)ReentrantLock:lock()。
2)FairSync:lock()。
3)AbstractQueuedSynchronizer:acquire(int arg)。
4)ReentrantLock:tryAcquire(int acquires)。
在第4步真正開始加鎖,下面是該方法的源代碼。

從上面源代碼中我們可以看出,加鎖方法首先讀volatile變量state。
在使用公平鎖時(shí),解鎖方法unlock()調(diào)用軌跡如下.
1)ReentrantLock:unlock()。
2)AbstractQueuedSynchronizer:release(int arg)。
3)Sync:tryRelease(int releases)。
在第3步真正開始釋放鎖,下面是該方法的源代碼。

從上面的源代碼可以看出,在釋放鎖的最后寫volatile變量state.
公平鎖在釋放鎖的最后寫volatile變量state,在獲取鎖時(shí)首先讀這個(gè)volatile變量.根據(jù)volatile變量的happens-before規(guī)則,釋放鎖的線程寫在volatile變量之前可見的共享變量,在獲取鎖的線程讀取同一個(gè)volatile變量后立即變得對(duì)獲取鎖的線程可見.
非公平鎖
非公平鎖的釋放和公平鎖完全一樣,所以這里僅僅分析非公平鎖的獲取。使用非公平鎖時(shí),加鎖方法lock()調(diào)用軌跡如下。
1)ReentrantLock:lock()。
2)NonfairSync:lock()。
3)AbstractQueuedSynchronizer:compareAndSetState(int expect,int update)。
在第3步真正開始加鎖,下面是該方法的源代碼。

該方法以原子操作的方式更新state變量,把Java的compareAndSet()方法調(diào)用簡(jiǎn)稱為CAS。JDK文檔對(duì)該方法的說明如下:如果當(dāng)前狀態(tài)值等于預(yù)期值,則以原子方式將同步狀態(tài)設(shè)置為給定的更新值。此操作具有volatile讀和寫的內(nèi)存語(yǔ)義。
編譯器不會(huì)對(duì)volatile讀與volatile讀后面的任意內(nèi)存操作重排序;編譯器不會(huì)對(duì)volatile寫與volatile寫前面的任意內(nèi)存操作重排序。組合這兩個(gè)條件,意味著為了同時(shí)實(shí)現(xiàn)volatile讀和volatile寫的內(nèi)存語(yǔ)義,編譯器不能對(duì)CAS與CAS前面和后面的任意內(nèi)存操作重排序.
公平鎖和非公平鎖的內(nèi)存語(yǔ)義小結(jié)
·公平鎖和非公平鎖釋放時(shí),最后都要寫一個(gè)volatile變量state。
·公平鎖獲取時(shí),首先會(huì)去讀volatile變量。
·非公平鎖獲取時(shí),首先會(huì)用CAS更新volatile變量,這個(gè)操作同時(shí)具有volatile讀和volatile寫的內(nèi)存語(yǔ)義。
鎖釋放-獲取的內(nèi)存語(yǔ)義的實(shí)現(xiàn)至少有 下面兩種方式
- 利用volatile變量的寫-讀所具有的內(nèi)存語(yǔ)義。
- 利用CAS所附帶的volatile讀和volatile寫的內(nèi)存語(yǔ)義。
concurrent包的實(shí)現(xiàn)
由于Java的CAS同時(shí)具有volatile讀和volatile寫的內(nèi)存語(yǔ)義,因此Java線程之間的通信現(xiàn)在有了下面4種方式。
- A線程寫volatile變量,隨后B線程讀這個(gè)volatile變量
- A線程寫volatile變量,隨后B線程用CAS更新這個(gè)volatile變量
- A線程用CAS更新一個(gè)volatile變量,隨后B線程用CAS更新這個(gè)volatile變量
- A線程用CAS更新一個(gè)volatile變量,隨后B線程讀這個(gè)volatile變量
Java的CAS會(huì)使用現(xiàn)代處理器上提供的高效機(jī)器級(jí)別的原子指令,這些原子指令以原子方式對(duì)內(nèi)存執(zhí)行讀-改-寫操作,這是在多處理器中實(shí)現(xiàn)同步的關(guān)鍵(從本質(zhì)上來(lái)說,能夠支持原子性讀-改-寫指令的計(jì)算機(jī),是順序計(jì)算圖靈機(jī)的異步等價(jià)機(jī)器,因此任何現(xiàn)代的多處理器都會(huì)去支持某種能對(duì)內(nèi)存執(zhí)行原子性讀-改-寫操作的原子指令)。同時(shí),volatile變量的讀/寫和CAS可以實(shí)現(xiàn)線程之間的通信。把這些特性整合在一起,就形成了整個(gè)concurrent包得以實(shí)現(xiàn)的基石。如果我們仔細(xì)分析concurrent包的源代碼實(shí)現(xiàn),會(huì)發(fā)現(xiàn)一個(gè)通用化的實(shí)現(xiàn)模式。
- 聲明共享變量為volatile變量
- 使用CAS的院子條件更新來(lái)實(shí)現(xiàn)線程之間的同步,同時(shí)配合volatile的讀/寫和CAS所具有的volatile讀和寫的內(nèi)存語(yǔ)義來(lái)實(shí)現(xiàn)線程之間的通信.
AQS,非阻塞數(shù)據(jù)結(jié)構(gòu)和原子變量類(java.util.concurrent.atomic包中的類),這些concurrent包中的基礎(chǔ)類都是使用這種模式來(lái)實(shí)現(xiàn)的,而concurrent包中的高層類又是依賴于這些基礎(chǔ)類來(lái)實(shí)現(xiàn)的

參考書籍:<>
來(lái)源:https://www./content-3-439001.html
|