java內(nèi)存模型 JMM
658
2025-04-01
volatile寫的內(nèi)存語(yǔ)義:當(dāng)寫一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存。
volatile讀的內(nèi)存語(yǔ)義如下:當(dāng)讀一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無(wú)效。線程接下來(lái)將從主內(nèi)存中讀取共享變量。
線程A寫一個(gè)volatile變量,實(shí)質(zhì)上是線程A向接下來(lái)將要讀這個(gè)volatile變量的某個(gè)線程發(fā)出了(其對(duì)共享變量所做修改的)消息。
線程B讀一個(gè)volatile變量,實(shí)質(zhì)上是線程B接收了之前某個(gè)線程發(fā)出的(在寫這個(gè)volatile變量之前對(duì)共享變量所做修改的)消息。
線程A寫一個(gè)volatile變量,隨后線程B讀這個(gè)volatile變量,這個(gè)過程實(shí)質(zhì)上是線程A通過主內(nèi)存向線程B發(fā)送消息。
基于保守策略的JMM內(nèi)存屏障插入策略:
在每個(gè)volatile寫操作的前面插入一個(gè)StoreStore屏障。
在每個(gè)volatile寫操作的后面插入一個(gè)StoreLoad屏障。
在每個(gè)volatile讀操作的后面插入一個(gè)LoadLoad屏障。
在每個(gè)volatile讀操作的后面插入一個(gè)LoadStore屏障。
public class VolatileBarrierExample { int a; volatile int v1 = 1; volatile int v2 = 2; void readAndWrite() { int i = v1; // 第一個(gè)volatile讀 int j = v2; // 第二個(gè)volatile讀 a = i + j; // 普通寫 v1 = i + 1; // 第一個(gè)volatile寫 v2 = j * 2; // 第二個(gè) volatile寫 } }
線程A釋放一個(gè)鎖,實(shí)質(zhì)上是線程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ā)送消息。
ReentrantLock分為公平鎖和非公平鎖,我們首先分析公平鎖
使用公平鎖時(shí),加鎖方法lock()調(diào)用軌跡如下。
1)ReentrantLock:lock()。
2)FairSync:lock()。
3)AbstractQueuedSynchronizer:acquire(int arg)。
4)ReentrantLock:tryAcquire(int acquires)。
private volatile int state; protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
從上面源代碼中我們可以看出,加鎖方法首先讀volatile變量state。
在使用公平鎖時(shí),解鎖方法unlock()調(diào)用軌跡如下。
1)ReentrantLock:unlock()。
2)AbstractQueuedSynchronizer:release(int arg)。
3)Sync:tryRelease(int releases)。
protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
從上面的源代碼可以看出,在釋放鎖的最后setState 寫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)。
/** * Sync object for non-fair locks */ static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
該方法以原子操作的方式更新state變量,Java的compareAndSet()方法調(diào)用簡(jiǎn)稱為CAS。JDK文檔對(duì)該方法的說(shuō)明如下:如果當(dāng)前狀態(tài)值等于預(yù)期值,則以原子方式將同步狀態(tài)設(shè)置為給定的更新值
cas原理:如果程序是在多處理器上運(yùn)行 就在指令加上lock前綴
lock前綴作用:
1)確保對(duì)內(nèi)存的讀-改-寫操作原子執(zhí)行。在Pentium及Pentium之前的處理器中,帶有l(wèi)ock前綴的指令在執(zhí)行期間會(huì)鎖住總線,使得其他處理器暫時(shí)無(wú)法通過總線訪問內(nèi)存。很顯然,這會(huì)帶來(lái)昂貴的開銷。從Pentium 4、Intel Xeon及P6處理器開始,Intel使用緩存鎖定(Cache Locking)來(lái)保證指令執(zhí)行的原子性。緩存鎖定將大大降低lock前綴指令的執(zhí)行開銷。
2)禁止該指令,與之前和之后的讀和寫指令重排序。
3)把寫緩沖區(qū)中的所有數(shù)據(jù)刷新到內(nèi)存中。
所以CAS同時(shí)具有volatile讀和volatile寫的內(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)至少有下面兩種方式:
1)利用volatile變量的寫-讀所具有的內(nèi)存語(yǔ)義。
2)利用CAS所附帶的volatile讀和volatile寫的內(nèi)存語(yǔ)義。
current并發(fā)包
由于Java的CAS同時(shí)具有volatile讀和volatile寫的內(nèi)存語(yǔ)義,因此Java線程之間的通信現(xiàn)
在有了下面4種方式。
1)A線程寫volatile變量,隨后B線程讀這個(gè)volatile變量。
2)A線程寫volatile變量,隨后B線程用CAS更新這個(gè)volatile變量。
3)A線程用CAS更新一個(gè)volatile變量,隨后B線程用CAS更新這個(gè)volatile變量。
4)A線程用CAS更新一個(gè)volatile變量,隨后B線程讀這個(gè)volatile變量。
concurrent包的源代碼通用化的實(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)的
Java 任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。