ReentrantLock 鎖機(jī)制(非公平鎖)源碼

ReentrantLock和synchronized一樣加鎖方式,獨(dú)占的獲取同步的對(duì)象鎖。

? ? 1. 簡介

? ? ReentrantLock內(nèi)部由Sync類實(shí)例實(shí)現(xiàn)其中ReentrantLock.FairSync,ReentrantLock.NonfairSync兩個(gè)實(shí)現(xiàn),也就是常說的公平鎖和不公平鎖。

而Sync繼承于AbstractQueuedSynchronizer。AbstractQueuedSynchronizer這個(gè)類真的很難也很復(fù)雜,是構(gòu)建鎖以及實(shí)現(xiàn)其他相關(guān)同步類的基礎(chǔ)框架。本篇文章只能說是對(duì)自己看這個(gè)類的一點(diǎn)點(diǎn)理解和記錄,若有錯(cuò),請(qǐng)批評(píng)指正。

? ? 2. ReentrantLock類的lock()方法

? ? 由于鎖Lock的實(shí)現(xiàn)都是委托給AbstractQueuedSynchronizer來實(shí)現(xiàn)的。因此,就將分析ReenterantLock類中如何獲取鎖和如何釋放鎖來理解。

public voidJava.util.concurrent.locks.ReentrantLock.lock()

如果該鎖沒有被另一個(gè)線程保持,則獲取該鎖并立即返回,并將鎖的保持計(jì)數(shù)器設(shè)置為1.

如果當(dāng)前線程已經(jīng)保持該鎖,則將保持計(jì)數(shù)加1,并且該方法立即返回。

如果該鎖被另一個(gè)線程保持,則出于線程調(diào)度的目的,禁用該線程,并且在獲得鎖之前,該線程一直處于休眠狀態(tài),此時(shí)鎖保持計(jì)數(shù)被設(shè)置為1.

? ? 保持計(jì)數(shù)就是AQS類的state變量。

默認(rèn)ReentrantLock構(gòu)造器

public ReentrantLock() {

? ? sync =new NonfairSync();

}


默認(rèn)是非公平鎖,看看NonfairSync類下的lock方法:

final void lock() {

? ? if (compareAndSetState(0,1))

? ? ? ? setExclusiveOwnerThread(Thread.currentThread());

? ? else

? ? ? ? acquire(1);

}

? ? compareAndSetState(0, 1) 這個(gè)是嘗試獲取鎖,把state的狀態(tài)從0改為1表示取得鎖。這個(gè)方法原理是CAS,CAS有3個(gè)操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否則什么都不做!

? ? setExclusiveOwnerThread()設(shè)置獲取鎖的線程就是當(dāng)前線程.

? ? 具體調(diào)用的是:

protected final boolean compareAndSetState (int expect, int update) {

? ? return unsafe.compareAndSwapInt (this, stateOffset, expect, update);

}

? ? unsafe的compareAndSwapInt方法是native的.

但是我們更關(guān)注的是,當(dāng)前線程申請(qǐng)鎖不成功的時(shí)候是怎么做的.可以看到是AQS中的acquire(1);

public final void acquire(int arg) {

//先嘗試獲取鎖,如果獲取鎖失敗了,acquireQueued則執(zhí)行

? ? if( !tryAcquire(arg) && acquireQueued( addWaiter(Node.EXCLUSIVE), arg))

? ? ? ? selfInterrupt();

}

后面的方法。注意&&,前面為true后面才執(zhí)行。獲取鎖失敗后,會(huì)將該線程加入等待隊(duì)列

? ? 這個(gè)看起來比較復(fù)雜,我們分解以下4個(gè)步驟。

1、如果tryAcquire(arg)成功,那就沒有問題,已經(jīng)拿到鎖,整個(gè)lock()過程就結(jié)束了。如果失敗進(jìn)行操作2。

2、調(diào)用addWaiter方法:將當(dāng)前線程創(chuàng)建一個(gè)獨(dú)占節(jié)點(diǎn)(Node)并且此節(jié)點(diǎn)加入CHL隊(duì)列末尾。進(jìn)行操作3。

3、自旋嘗試獲取鎖,失敗根據(jù)前一個(gè)節(jié)點(diǎn)來決定是否掛起(park()),直到成功獲取到鎖。進(jìn)行操作4。

4、如果當(dāng)前線程已經(jīng)中斷過,那么就中斷當(dāng)前線程(清除中斷位)。

下面我們對(duì)acquire方法中調(diào)用的其它方法一一進(jìn)行分析。

? ? tryAcquire(acquires)

protected final boolean tryAcquire(int acquires) {

? ? return nonfairTryAcquire(acquires);

}

final boolean nonfairTryAcquire (int acquires) {

? ? final Thread current = Thread.currentThread();?

? ? int c = getState(); ?//對(duì)于AQS存在一個(gè)state來描述當(dāng)前有多少線程持有鎖

? ? /*

? ? ? ? 如果c等于零,則沒有線程持有鎖,則將鎖給當(dāng)前線程即可

? ? ? ? 如果c不等于,說明當(dāng)前線程已經(jīng)獲取了鎖,這里是當(dāng)前線程再次要獲得鎖,所以state ? ? ? ? ? 要繼續(xù)+1

? ? */

? ? if(c ==0) {

? ? ? ? if(compareAndSetState(0, acquires)) {

? ? ? ? ? ? setExclusiveOwnerThread(current);

? ? ? ? ? ? return true;

? ? ? ? }

? ? }

? ? else if (current == getExclusiveOwnerThread()) { //判斷當(dāng)前線程是否為AQS的獨(dú)占線程

? ? ? ? int nextc = c + acquires;

? ? ? ? if( nextc < 0 ) // overflow?

? ? ? ? ? ? throw new Error("Maximum lock count exceeded");

? ? ? ? setState(nextc);

? ? ? ? return true;

? ? }

? ? //獲取鎖失敗,返回false。

? ? return false;


? ? tryAcquire的邏輯是這樣的, c = getState() 就是當(dāng)前沒有鎖競爭的時(shí)候,會(huì)再嘗試去獲得鎖.

current == getExclusiveOwnerThread()):當(dāng)前線程已經(jīng)獲取鎖了,那么鎖的記數(shù)加1.

addWaiter(mode)

? ? 如果tryAcquire沒有成功,? 就執(zhí)行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

? ? addWaiter是把線程和線程的狀態(tài)信息封裝到一個(gè)node對(duì)象,加入CHL阻塞鏈表,Node封裝了各種線程狀態(tài):

static final int CANCELLED = 1; //這個(gè)狀態(tài)說明該節(jié)點(diǎn)已經(jīng)被取消。

static final int SIGNAL = -1; //這個(gè)狀態(tài)說明該節(jié)點(diǎn)后續(xù)有阻塞的節(jié)點(diǎn) 。

static final int CONDITION = -2; //表明該線程被處于條件隊(duì)列,就是因?yàn)檎{(diào)用了Condition.await而被阻塞

static final int PROPAGATE(-3); //傳播共享鎖

0:0代表無狀態(tài)

? ? 其實(shí)就是把當(dāng)前線程放到一個(gè)鏈表的末尾去.具體怎么放有點(diǎn)講究,而且用到了無限循環(huán),也就是說,一定要把線程放進(jìn)鏈表的!

? ? static final Node EXCLUSIVE = null; //獨(dú)占節(jié)點(diǎn)模式

? ? static final Node SHARED = new Node(); //共享節(jié)點(diǎn)模式

? ? addWaiter(mode)中的mode就是節(jié)點(diǎn)模式,也就是共享鎖還是獨(dú)占鎖模式。

privateNode addWaiter(Node mode) {

? ? //當(dāng)前線程節(jié)點(diǎn),線程的狀態(tài)信息封裝到一個(gè)node對(duì)象。

? ? Node node =new Node(Thread.currentThread(), mode);

? ? Node pred = tail;

? ? //判斷有沒有尾節(jié)點(diǎn)(也就是前面是否有等待線程)。如果有尾節(jié)點(diǎn),則將當(dāng)前線程的節(jié)點(diǎn)插入到隊(duì)列的尾部,也就是將當(dāng)前線程變成尾節(jié)點(diǎn)。

? ? if(pred != null) {

? ? ? ? node.prev = pred;

? ? ? ? //CAS的操作。

? ? ? ? if(compareAndSetTail(pred, node)) {

? ? ? ? ? ? pred.next = node;

? ? ? ? ? ? return node;

? ? ? ? }

? ? }

? ? //如果沒有尾節(jié)點(diǎn),說明前面還未有等待線程。調(diào)用下面的方法,創(chuàng)建等待隊(duì)列

? ? enq(node);

? ? return node;

}

總而言之,addWaiter的目的就是通過CAS把當(dāng)前現(xiàn)在追加到隊(duì)尾,并返回包裝后的Node實(shí)例。

acquireQueued(node,arg)

//這個(gè)方法是不斷地獲取鎖,直到成功的獲取鎖,或者阻塞當(dāng)前線程

acquireQueued也是個(gè)無限循環(huán)。就是說要么獲取到鎖,要么中斷當(dāng)前線程。

1. acquireQueued方法在無限循環(huán)內(nèi)獲取前繼節(jié)點(diǎn),判斷前繼節(jié)點(diǎn)是否為head,是就再嘗試 ? ? 獲取鎖,之后前繼節(jié)點(diǎn)dequeue出隊(duì),node成為head。

2. 前繼節(jié)點(diǎn)p != head 或者 前繼節(jié)點(diǎn)p == head但是tryAcquire失敗了,那么應(yīng)該阻塞當(dāng)前線 ? ? 程等待前繼喚醒。阻塞之前會(huì)再重試一次,還需要設(shè)置前繼的waitStaus為SIGNAL。

3. 調(diào)用shouldParkAfterFailedAcquire方法,后面的方法是對(duì)當(dāng)前線程進(jìn)行阻塞并且判斷是 ? ? ? 否中斷。這里注意的是,如果一個(gè)線程在等待鎖期間這個(gè)線程被中斷了,這里會(huì)將 ? ? ? ? ? ? ? interrupted賦為true,但是并不return。這個(gè)還一直進(jìn)行for循環(huán),知道這個(gè)線程獲得了鎖, ? ? 所以lock()方法不能立即響應(yīng)中斷,必須等線程獲得了鎖才可以響應(yīng)中斷。對(duì)應(yīng)的可以立 ? ? ? 即響應(yīng)中斷的方法為lockInterruptibly()方法。

//阻塞當(dāng)前線程

private final boolean parkAndCheckInterrupt() {

? ? //阻塞當(dāng)前線程

? ? LockSupport.park(this);

? ? return Thread.interrupted();


unLock()方法

public void unlock() {

? ? sync.release(1);

非公平鎖release方法


非公平鎖tryRelease方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容