前一篇博客簡(jiǎn)單介紹了ReentrantLock的定義和與synchronized的區(qū)別,下面跟隨LZ的筆記來(lái)扒扒ReentrantLock的lock方法。我們知道ReentrantLock有公平鎖、非公平鎖之分,所以lock()我也已公平鎖、非公平鎖來(lái)進(jìn)行闡述。首先我們來(lái)看ReentrantLock的結(jié)構(gòu)【圖來(lái)自Java多線(xiàn)程系列--“JUC鎖”03之 公平鎖(一)】:

從上圖我們可以看到,ReentrantLock實(shí)現(xiàn)Lock接口,Sync與ReentrantLock是組合關(guān)系,且FairSync(公平鎖)、NonfairySync(非公平鎖)是Sync的子類(lèi)。Sync繼承AQS(AbstractQueuedSynchronizer)。在具體分析lock時(shí),我們需要了解幾個(gè)概念:
AQS(AbstractQueuedSynchronizer):為java中管理鎖的抽象類(lèi)。該類(lèi)為實(shí)現(xiàn)依賴(lài)于先進(jìn)先出 (FIFO) 等待隊(duì)列的阻塞鎖和相關(guān)同步器(信號(hào)量、事件,等等)提供一個(gè)框架。該類(lèi)提供了一個(gè)非常重要的機(jī)制,在JDK API中是這樣描述的:為實(shí)現(xiàn)依賴(lài)于先進(jìn)先出 (FIFO) 等待隊(duì)列的阻塞鎖和相關(guān)同步器(信號(hào)量、事件,等等)提供一個(gè)框架。此類(lèi)的設(shè)計(jì)目標(biāo)是成為依靠單個(gè)原子 int 值來(lái)表示狀態(tài)的大多數(shù)同步器的一個(gè)有用基礎(chǔ)。子類(lèi)必須定義更改此狀態(tài)的受保護(hù)方法,并定義哪種狀態(tài)對(duì)于此對(duì)象意味著被獲取或被釋放。假定這些條件之后,此類(lèi)中的其他方法就可以實(shí)現(xiàn)所有排隊(duì)和阻塞機(jī)制。子類(lèi)可以維護(hù)其他狀態(tài)字段,但只是為了獲得同步而只追蹤使用 getState()、setState(int) 和 compareAndSetState(int, int) 方法來(lái)操作以原子方式更新的 int 值。 這么長(zhǎng)的話(huà)用一句話(huà)概括就是:維護(hù)鎖的當(dāng)前狀態(tài)和線(xiàn)程等待列表。
CLH:AQS中“等待鎖”的線(xiàn)程隊(duì)列。我們知道在多線(xiàn)程環(huán)境中我們?yōu)榱吮Wo(hù)資源的安全性常使用鎖將其保護(hù)起來(lái),同一時(shí)刻只能有一個(gè)線(xiàn)程能夠訪(fǎng)問(wèn),其余線(xiàn)程則需要等待,CLH就是管理這些等待鎖的隊(duì)列。
CAS(compare and swap):比較并交換函數(shù),它是原子操作函數(shù),也就是說(shuō)所有通過(guò)CAS操作的數(shù)據(jù)都是以原子方式進(jìn)行的。
公平鎖(FairSync):locklock()定義如下:
final void lock() { acquire(1); }lock()內(nèi)部調(diào)用acquire(1),為何是”1”呢?首先我們知道ReentrantLock是獨(dú)占鎖,1表示的是鎖的狀態(tài)state。對(duì)于獨(dú)占鎖而言,如果所處于可獲取狀態(tài),其狀態(tài)為0,當(dāng)鎖初次被線(xiàn)程獲取時(shí)狀態(tài)變成1。
acquire()是AbstractQueuedSynchronizer中的方法,其源碼如下:
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }從該方法的實(shí)現(xiàn)中我們可以看出,它做了非常多的工作,具體工作我們先晾著,先看這些方法的實(shí)現(xiàn):
tryAcquiretryAcquire方法是在FairySync中實(shí)現(xiàn)的,其源代碼如下:
PRotected final boolean tryAcquire(int acquires) { //當(dāng)前線(xiàn)程 final Thread current = Thread.currentThread(); //獲取鎖狀態(tài)state int c = getState(); /* * 當(dāng)c==0表示鎖沒(méi)有被任何線(xiàn)程占用,在該代碼塊中主要做如下幾個(gè)動(dòng)作: * 則判斷“當(dāng)前線(xiàn)程”是不是CLH隊(duì)列中的第一個(gè)線(xiàn)程線(xiàn)程(hasQueuedPredecessors), * 若是的話(huà),則獲取該鎖,設(shè)置鎖的狀態(tài)(compareAndSetState), * 并切設(shè)置鎖的擁有者為“當(dāng)前線(xiàn)程”(setExclusiveOwnerThread)。 */ if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } /* * 如果c != 0,表示該鎖已經(jīng)被線(xiàn)程占有,則判斷該鎖是否是當(dāng)前線(xiàn)程占有,若是設(shè)置state,否則直接返回false */ else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
在這里我們可以肯定tryAcquire主要是去嘗試獲取鎖,獲取成功則設(shè)置鎖狀態(tài)并返回true,否則返回false。
hasQueuedPredecessors:"當(dāng)前線(xiàn)程"是不是在CLH隊(duì)列的隊(duì)首,來(lái)返回AQS中是不是有比“當(dāng)前線(xiàn)程”等待更久的線(xiàn)程(公平鎖)。
public final boolean hasQueuedPredecessors() { Node t = tail; Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }Node是AbstractQueuedSynchronizer的內(nèi)部類(lèi),它代表著CLH列表的一個(gè)線(xiàn)程節(jié)點(diǎn)。對(duì)于Node以后LZ會(huì)詳細(xì)闡述的。
compareAndSetState:設(shè)置鎖狀態(tài)
protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }compareAndSwapInt() 是sun.misc.Unsafe類(lèi)中的一個(gè)本地方法。對(duì)此,我們需要了解的是 compareAndSetState(expect, update) 是以原子的方式操作當(dāng)前線(xiàn)程;若當(dāng)前線(xiàn)程的狀態(tài)為expect,則設(shè)置它的狀態(tài)為update。
setExclusiveOwnerThread:設(shè)置當(dāng)前線(xiàn)程為鎖的擁有者
protected final void setExclusiveOwnerThread(Thread t) { exclusiveOwnerThread = t; }addWaiter(Node.EXCLUSIVE) private Node addWaiter(Node mode) { //new 一個(gè)Node節(jié)點(diǎn) Node node = new Node(Thread.currentThread(), mode); //CLH隊(duì)列尾節(jié)點(diǎn) Node pred = tail; //CLH尾節(jié)點(diǎn)!= null,表示CLH隊(duì)列 != null,則將線(xiàn)程加入到CLH隊(duì)列隊(duì)尾 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //若CLH隊(duì)列為空,則調(diào)用enq()新建CLH隊(duì)列,然后再將“當(dāng)前線(xiàn)程”添加到CLH隊(duì)列中。 enq(node); return node; }addWaiter()主要是將當(dāng)前線(xiàn)程加入到CLH隊(duì)列隊(duì)尾。其中compareAndSetTail和enq的源代碼如下:
/** * 判斷CLH隊(duì)列的隊(duì)尾是不是為expect,是的話(huà),就將隊(duì)尾設(shè)為update * @param expect * @param update * @return */ private final boolean compareAndSetTail(Node expect, Node update) { return unsafe.compareAndSwapObject(this, tailOffset, expect, update); } /** * 如果CLH隊(duì)列為空,則新建一個(gè)CLH表頭;然后將node添加到CLH末尾。否則,直接將node添加到CLH末尾 * @param node * @return */ private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }addWaiter的實(shí)現(xiàn)比較簡(jiǎn)單且實(shí)現(xiàn)功能明了:當(dāng)前線(xiàn)程加入到CLH隊(duì)列隊(duì)尾。
acquireQueuedfinal boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { //線(xiàn)程中斷標(biāo)志位 boolean interrupted = false; for (;;) { //上一個(gè)節(jié)點(diǎn),因?yàn)閚ode相當(dāng)于當(dāng)前線(xiàn)程,所以上一個(gè)節(jié)點(diǎn)表示“上一個(gè)等待鎖的線(xiàn)程” final Node p = node.predecessor(); /* * 如果當(dāng)前線(xiàn)程是head的直接后繼則嘗試獲取鎖 * 這里不會(huì)和等待隊(duì)列中其它線(xiàn)程發(fā)生競(jìng)爭(zhēng),但會(huì)和嘗試獲取鎖且尚未進(jìn)入等待隊(duì)列的線(xiàn)程發(fā)生競(jìng)爭(zhēng)。這是非公平鎖和公平鎖的一個(gè)重要區(qū)別。 */ if (p == head && tryAcquire(arg)) { setHead(node); //將當(dāng)前節(jié)點(diǎn)設(shè)置設(shè)置為頭結(jié)點(diǎn) p.next = null; failed = false; return interrupted; } /* 如果不是head直接后繼或獲取鎖失敗,則檢查是否要阻塞當(dāng)前線(xiàn)程,是則阻塞當(dāng)前線(xiàn)程 * shouldParkAfterFailedAcquire:判斷“當(dāng)前線(xiàn)程”是否需要阻塞 * parkAndCheckInterrupt:阻塞當(dāng)前線(xiàn)程 */ if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }在這個(gè)for循環(huán)中,LZ不是很明白為什么要加p==head,Java多線(xiàn)程系列--“JUC鎖”03之 公平鎖(一)這篇博客有一個(gè)較好的解釋如下:
p == head && tryAcquire(arg) 首先,判斷“前繼節(jié)點(diǎn)”是不是CHL表頭。如果是的話(huà),則通過(guò)tryAcquire()嘗試獲取鎖。 其實(shí),這樣做的目的是為了“讓當(dāng)前線(xiàn)程獲取鎖”,但是為什么需要先判斷p==head呢?理解這個(gè)對(duì)理解“公平鎖”的機(jī)制很重要,因?yàn)檫@么做的原因就是為了保證公平性! (a) 前面,我們?cè)趕houldParkAfterFailedAcquire()我們判斷“當(dāng)前線(xiàn)程”是否需要阻塞; (b) 接著,“當(dāng)前線(xiàn)程”阻塞的話(huà),會(huì)調(diào)用parkAndCheckInterrupt()來(lái)阻塞線(xiàn)程。當(dāng)線(xiàn)程被解除阻塞的時(shí)候,我們會(huì)返回線(xiàn)程的中斷狀態(tài)。而線(xiàn)程被解決阻塞,可能是由于“線(xiàn)程被中斷”,也可能是由于“其它線(xiàn)程調(diào)用了該線(xiàn)程的unpark()函數(shù)”。 (c) 再回到p==head這里。如果當(dāng)前線(xiàn)程是因?yàn)槠渌€(xiàn)程調(diào)用了unpark()函數(shù)而被喚醒,那么喚醒它的線(xiàn)程,應(yīng)該是它的前繼節(jié)點(diǎn)所對(duì)應(yīng)的線(xiàn)程(關(guān)于這一點(diǎn),后面在“釋放鎖”的過(guò)程中會(huì)看到)。 OK,是前繼節(jié)點(diǎn)調(diào)用unpark()喚醒了當(dāng)前線(xiàn)程! 此時(shí),再來(lái)理解p==head就很簡(jiǎn)單了:當(dāng)前繼節(jié)點(diǎn)是CLH隊(duì)列的頭節(jié)點(diǎn),并且它釋放鎖之后;就輪到當(dāng)前節(jié)點(diǎn)獲取鎖了。然后,當(dāng)前節(jié)點(diǎn)通過(guò)tryAcquire()獲取鎖;獲取成功的話(huà),通過(guò)setHead(node)設(shè)置當(dāng)前節(jié)點(diǎn)為頭節(jié)點(diǎn),并返回。 總之,如果“前繼節(jié)點(diǎn)調(diào)用unpark()喚醒了當(dāng)前線(xiàn)程”并且“前繼節(jié)點(diǎn)是CLH表頭”,此時(shí)就是滿(mǎn)足p==head,也就是符合公平性原則的。否則,如果當(dāng)前線(xiàn)程是因?yàn)椤熬€(xiàn)程被中斷”而喚醒,那么顯然就不是公平了。這就是為什么說(shuō)p==head就是保證公平性!
在該方法中有兩個(gè)方法比較重要,shouldParkAfterFailedAcquire和parkAndCheckInterrupt,其中
shouldParkAfterFailedAcquire:判斷“當(dāng)前線(xiàn)程”是否需要阻塞,源碼如下:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //當(dāng)前節(jié)點(diǎn)的狀態(tài) int ws = pred.waitStatus; if (ws == Node.SIGNAL) return true; if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }waitStatus是節(jié)點(diǎn)Node定義的,她是標(biāo)識(shí)線(xiàn)程的等待狀態(tài),他主要有如下四個(gè)值:
CANCELLED = 1:線(xiàn)程已被取消;
SIGNAL = -1:當(dāng)前線(xiàn)程的后繼線(xiàn)程需要被unpark(喚醒);
CONDITION = -2 :線(xiàn)程(處在Condition休眠狀態(tài))在等待Condition喚醒;
PROPAGATE = –3:(共享鎖)其它線(xiàn)程獲取到“共享鎖”.
有了這四個(gè)狀態(tài),我們?cè)賮?lái)分析上面代碼,當(dāng)ws == SIGNAL時(shí)表明當(dāng)前節(jié)點(diǎn)需要unpark(喚醒),直接返回true,當(dāng)ws > 0 (CANCELLED),表明當(dāng)前節(jié)點(diǎn)已經(jīng)被取消了,則通過(guò)回溯的方法(do{}while())向前找到一個(gè)非CANCELLED的節(jié)點(diǎn)并返回false。其他情況則設(shè)置該節(jié)點(diǎn)為SIGNAL狀態(tài)。我們?cè)倩氐絠f (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()),p是當(dāng)前節(jié)點(diǎn)的前繼節(jié)點(diǎn),當(dāng)該前繼節(jié)點(diǎn)狀態(tài)為SIGNAL時(shí)返回true,表示當(dāng)前線(xiàn)程需要阻塞,則調(diào)用parkAndCheckInterrupt()阻塞當(dāng)前線(xiàn)程。
parkAndCheckInterrupt:阻塞當(dāng)前線(xiàn)程,并且返回“線(xiàn)程被喚醒之后”的中斷狀態(tài),源碼如下:
private final boolean parkAndCheckInterrupt() { //通過(guò)LockSupport的park()阻塞“當(dāng)前線(xiàn)程”。 LockSupport.park(this); return Thread.interrupted(); }從上面我們可以總結(jié),acquireQueued()是當(dāng)前線(xiàn)程會(huì)根據(jù)公平性原則來(lái)進(jìn)行阻塞等待,直到獲取鎖為止;并且返回當(dāng)前線(xiàn)程在等待過(guò)程中有沒(méi)有并中斷過(guò)。
selfInterruptprivate static void selfInterrupt() { Thread.currentThread().interrupt(); }selfInterrupt()產(chǎn)生一個(gè)中斷。如果在acquireQueued()中當(dāng)前線(xiàn)程被中斷過(guò),則需要產(chǎn)生一個(gè)中斷。
Fairy lock()總結(jié)我們?cè)倏碼cquire()源碼:
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }首先通過(guò)tryAcquire方法嘗試獲取鎖,如果成功直接返回,否則通過(guò)acquireQueued()再次獲取。在acquireQueued()中會(huì)先通過(guò)addWaiter將當(dāng)前線(xiàn)程加入到CLH隊(duì)列的隊(duì)尾,在CLH隊(duì)列中等待。在等待過(guò)程中線(xiàn)程處于休眠狀態(tài),直到成功獲取鎖才會(huì)返回。如下:

非公平鎖NonfairSync的lock()與公平鎖的lock()在獲取鎖的流程上是一直的,但是由于它是非公平的,所以獲取鎖機(jī)制還是有點(diǎn)不同。通過(guò)前面我們了解到公平鎖在獲取鎖時(shí)采用的是公平策略(CLH隊(duì)列),而非公平鎖則采用非公平策略它無(wú)視等待隊(duì)列,直接嘗試獲取。如下:
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }lock()通過(guò)compareAndSetState嘗試設(shè)置所狀態(tài),若成功直接將鎖的擁有者設(shè)置為當(dāng)前線(xiàn)程(簡(jiǎn)單粗暴),否則調(diào)用acquire()嘗試獲取鎖;
acquirepublic final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }在非公平鎖中acquire()的實(shí)現(xiàn)和公平鎖一模一樣,但是他們嘗試獲取鎖的機(jī)制不同(也就是tryAcquire()的實(shí)現(xiàn)不同)。
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }tryAcquire內(nèi)部調(diào)用nonfairyTryAcquire:
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }與公平鎖相比,非公平鎖的不同之處就體現(xiàn)在if(c==0)的條件代碼塊中:
//----------------非公平鎖----- if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //----------------公平鎖----- if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } }是否已經(jīng)發(fā)現(xiàn)了不同之處。公平鎖中要通過(guò)hasQueuedPredecessors()來(lái)判斷該線(xiàn)程是否位于CLH隊(duì)列中頭部,是則獲取鎖;而非公平鎖則不管你在哪個(gè)位置都直接獲取鎖。
參考文獻(xiàn):
1、Java多線(xiàn)程系列--“JUC鎖”03之 公平鎖(一)
2、ReentrantLock源碼之一lock方法解析(鎖的獲取)
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注