您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關如何在Java中實現鎖,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
1. 概述
鎖是Java并發編程中最重要的同步機制。鎖除了讓臨界區互斥執行外,還可以讓釋放鎖的線程獲取同一個鎖的線程發送消息。
2. 鎖的內存語義
當線程獲取鎖時, JMM會把線程對應的本地內存置為無效. 從而使得被監視器保護的臨界區的變量必須從主內存中讀取.
當線程釋放鎖時, JMM會把該線程對應的本地內存中的共享變量刷新到主內存中(并不是不釋放鎖就不刷新到主內存, 只是釋放鎖時把未刷新到主內存中的數據刷新到主內存).
鎖的內存語義與volatile的內存語義
鎖獲取與volatile讀有相同的內存語義.
鎖釋放與volatile寫有相同的內存語義.
內存語義總結
線程A釋放一個鎖, 實質上是線程A向接下來將要獲取這個鎖的某個線程發出了(線程A對共享變量所做修改的)消息.
線程B獲取一個鎖, 實質上是線程B接收了之前某個線程發出的(在釋放這個鎖之前對共享變量所做修改的)消息.
線程A釋放鎖, 隨后線程B獲取這個鎖, 這個過程實質上是線程A通過主內存向線程B發送消息.
3. 鎖內存語義的實現
下面以ReentrantLock為例, 獲取到鎖就是把state改為1(不考慮重入), 釋放鎖時改為0.
而加鎖的關鍵代碼就是
protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
該方法以原子操作的方式更新state變量, 本文把Java的compareAndSet()方法簡稱為CAS. JDK文檔對該方法的說明如下: 如果當前狀態值等于預期值, 則以原子方式將同步狀態設置為給定的更新值. 此操作具有volatile讀和寫的內存語義.
這里我們分別從編譯器和處理器的角度來分析: CAS如何同時具有volatile讀和volatile寫的內存語義.
我們知道, 編譯器不會對volatile讀與volatile讀后面的任意內存操作重排序; 編譯器不會對volatile寫與volatile寫前面的任意內存操作重排序. 組合這兩個條件, 意味著為了同時實現volatile讀和volatile寫的內存語義, 編譯器不能對CAS與CAS前面和后面的任意內存操作重排序.
下面我們來分析在常見的intel X86處理器中, CAS是如何同時具有volatile讀和volatile寫的內存語義的.
下面是sun.misc.Unsafe類的compareAndSwapInt()方法的源代碼.
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
可以看到, 這是一個本地方法調用. 這個本地方法在openjdk中依次調用的c++代碼為: unsafe.cpp, atomic.cpp 和 atomic_windows_x86.inline.hpp. 這個本地方法的最終實現在openjdk的如下位置: openjdk-7-fcs-src-b147-
27_jun_2011\openjdk\hotspot\src\os_cpu\windows_x86\vm\atomic_windows_x86.inline.hpp(對應于
Windows操作系統, X86處理器). 下面是對應于intel X86處理器的源代碼的片段.
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { // alternative for InterlockedCompareExchange int mp = os::is_MP(); __asm { mov edx, dest mov ecx, exchange_value mov eax, compare_value LOCK_IF_MP(mp) cmpxchg dword ptr [edx], ecx } }
如上面源代碼所示, 程序會根據當前處理器的類型來決定是否為cmpxchg指令添加lock前綴. 如果程序是在多處理器上運行, 就為cmpxchg指令加上lock前綴(Lock Cmpxchg). 反之, 如果程序是在單處理器上運行, 就省略lock前綴(單處理器自身會維護單處理器內的順序一致性, 不需要lock前綴提供的內存屏障效果).
intel的手冊對lock前綴的說明如下.
確保對內存的讀-改-寫操作原子執行. 在Pentium及Pentium之前的處理器中, 帶有lock前綴的指令在執行期間會鎖住總線, 使得其他處理器暫時無法通過總線訪問內存. 很顯然, 這會帶來昂貴的開銷. 從Pentium 4、Intel Xeon及P6處理器開始, Intel使用緩存鎖定(Cache Locking)
來保證指令執行的原子性. 緩存鎖定將大大降低lock前綴指令的執行開銷.
禁止該指令, 與之前和之后的讀和寫指令重排序.
把寫緩沖區中的所有數據刷新到內存中.
關于如何在Java中實現鎖就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。