前言
心血來潮想到要做一些源碼分析,想想從ReentrantLock類開始挺合適的闹获,就嘗試著先寫一篇吧学赛,廢話不多說進(jìn)入正題。
正文
說到ReentrantLock類首先肯定得看一下內(nèi)部類Sync斑匪,其定義如下(此處僅保留了一些重要部分呐籽,后面的源碼片段也會如此操作)
abstract static class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
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;
}
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;
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
}
除了Sync還有兩個繼承了Sync的內(nèi)部類,分別是NonfairSync和FairSync秤标,分別對應(yīng)了非公平鎖和公平鎖
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
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;
}
}
兩者的差別就是lock()方法和tryAcquire(int)方法的不同绝淡,其中l(wèi)ock()方法是Sync類定義的,而tryAcquire(int)方法是AbstractQueuedSynchronizer類定義的苍姜,這個類就是鎖類ReentrantLock和ReentrantReadWriteLock的核心,被稱之為AQS的類悬包,晚些再講解衙猪。這里順帶提一下,這種父類中定義一個抽象方法留待不同子類去具體實(shí)現(xiàn)的模式就是設(shè)計(jì)模式中的模板模式布近。繼續(xù)講源碼垫释,先來看一下ReentrantLock的lock()方法,可以看出實(shí)際的實(shí)現(xiàn)是由內(nèi)部類Sync的實(shí)現(xiàn)類來處理撑瞧,具體是FairSync還是NonfairSync的棵譬,就要看初始化時候的傳參了。
public void lock() {
sync.lock();
}
對比公平和非公平鎖的lock()實(shí)現(xiàn)预伺,acquire(1)這步是相同的订咸,不同的地方在于非公平鎖要進(jìn)行首次搶占,優(yōu)先執(zhí)行compareAndSetState(0, 1)酬诀。這個方法是屬于AQS的脏嚷,實(shí)現(xiàn)如下:
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
這里就碰到了非常經(jīng)典的CAS方法,簡單來說就是設(shè)置一個新值之前要判斷原值與期望值是否相同瞒御,相同才會設(shè)置新值父叙,并且會自旋重試,直到新值設(shè)置成功為止肴裙,CAS厲害在這一系列的動作都是原子的趾唱,避免了并發(fā)問題。同時unsafe對象是Unsafe類的實(shí)例蜻懦,該類就是jdk中專門用來調(diào)用這些封裝好的原子操作的甜癞。
此處CAS方法是第一次調(diào)用lock()方法時才會得到true,進(jìn)而執(zhí)行setExclusiveOwnerThread(Thread.currentThread())方法阻肩,其它情況下都是執(zhí)行acquire(1)方法去嘗試獲取鎖带欢。setExclusiveOwnerThread(Thread.currentThread())方法即是將當(dāng)前線程設(shè)置到exclusiveOwnerThread屬性上运授,即表示當(dāng)前線程獲得了鎖。
回過頭來看acquire(1)方法乔煞,該方法屬于AQS吁朦,具體實(shí)現(xiàn)如下
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
可以看出首當(dāng)其沖就是調(diào)了tryAcquire方法,之前剛好提過渡贾,該方法的具體實(shí)現(xiàn)是交由Sync的子類的逗宜。這里非公平鎖也是很有趣,實(shí)現(xiàn)tryAcquire方法就是又回過去調(diào)用了Sync的nonfairTryAcquire方法
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;
}
將這個方法和公平鎖的tryAcquire對比一下發(fā)現(xiàn)空骚,相似度99%纺讲。唯一不同的地方只有公平鎖中多了!hasQueuedPredecessors()這個判斷條件
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
說到hasQueuedPredecessors()這個方法,就不得不講AQS的設(shè)計(jì)結(jié)構(gòu)以及其中的CLH隊(duì)列囤屹,這里可以有一個簡單的概念就是AQS中維護(hù)了一個存放線程的鏈表熬甚,為了不擴(kuò)展的太遠(yuǎn),所以先來分析非公平鎖的流程肋坚。
首先根據(jù)getState()獲得當(dāng)前線程的狀態(tài)c乡括,可以看到c為0時可以獲得鎖,這與之前提到的非公平鎖的第一步操作又是相似度極高智厌。當(dāng)c不為0時诲泌,執(zhí)行current == getExclusiveOwnerThread()判斷,為true說明當(dāng)前線程是占用著該鎖的線程铣鹏,對c進(jìn)行累加操作直到c溢出時拋異常敷扫。此時跳回AQS的acquire方法,當(dāng)tryAcquire(1)的結(jié)果為false就有必要進(jìn)行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)的判斷了诚卸,接著就來看看這個方法葵第。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
這里入?yún)⑿枰粋€node,而方法調(diào)用時填的是addWaiter(Node.EXCLUSIVE)惨险,這里的EXCLUSIVE指的是獨(dú)占的意思羹幸,意味著這是一個獨(dú)占鎖的操作,只有一個線程可以占有鎖辫愉,與之相對的還有一種是SHARED栅受,即共享鎖,ReentrantReadWriteLock就是一種共享鎖恭朗,這個以后有機(jī)會再展開屏镊。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
接著看addWaiter()方法的實(shí)現(xiàn),這個方法概括起來就是將新生成的Node.EXCLUSIVE節(jié)點(diǎn)放到原來的隊(duì)尾節(jié)點(diǎn)的后面變成新的隊(duì)尾節(jié)點(diǎn)痰腮。這里用到了compareAndSetTail(pred, node)這個方法而芥,看一下實(shí)現(xiàn)
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
又看到了熟悉的unsafe的CAS操作,很明顯就是為了防止并發(fā)的情況膀值。
繼續(xù)拉回來看acquireQueued()方法棍丐,這里的主體是一個for循環(huán)误辑,還是一個死循環(huán),唯一跳出循環(huán)的可能是return interrupted這里歌逢,一看這條件我真的是倒吸一口涼氣巾钉,里面遞歸調(diào)用了tryAcquire()做為判斷條件,說實(shí)話這種操作不是對全局有很強(qiáng)的把控的話真的不敢隨便亂用秘案∨椴裕回過頭去看一眼tryAcquire()方法為true的條件是什么,當(dāng)然就是當(dāng)前線程必須要獲得鎖阱高。而p == head就是判斷p這個節(jié)點(diǎn)是不是頭節(jié)點(diǎn)赚导,那么這個p又是個,來看p = node.predecessor()的代碼
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
一看這方法其實(shí)就是返回上一個節(jié)點(diǎn)赤惊,p == head判斷的就是上一個節(jié)點(diǎn)是否是頭結(jié)點(diǎn)吼旧。連起來看就是當(dāng)上一節(jié)點(diǎn)是頭結(jié)點(diǎn)并且能獲得鎖的情況下結(jié)束循環(huán)返回false,如下的整個acquire()方法就結(jié)束了未舟,整個lock()過程也就結(jié)束了黍少。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
跳回for循環(huán)中,如果不能觸發(fā)return又是一直在做什么呢
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
首先看一下shouldParkAfterFailedAcquire(p, node)在干什么
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
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;
}
可以看到這個方法返回值是boolean類型的处面,根據(jù)描述當(dāng)線程需要阻塞時返回true。其中的判斷條件只有一個菩掏,就是pred.waitStatus魂角,即前一個節(jié)點(diǎn)的waitStatus狀態(tài)。
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
/*
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
volatile int waitStatus;
根據(jù)常量的定義和屬性的描述可以看出智绸,waitStatus的取值有0野揪,1,-1瞧栗,-2斯稳,-3這幾種情況,正常情況下初始值為0,并且這是一個volatile類型的屬性迹恐,為了保證可見性更新這個值需要CAS操作挣惰。然后看判斷條件,這里分成了三種情況殴边,第一種狀態(tài)為SIGNAL憎茂,即等待狀態(tài),線程需阻塞锤岸;第二種waitStatus>0竖幔,其實(shí)也只有CANCELLED這一種情況,這時做的事情是遞歸的將當(dāng)前節(jié)點(diǎn)連接到前一個節(jié)點(diǎn)的上一個節(jié)點(diǎn)是偷,也就是從這條鏈上去除掉所有CANCELLED狀態(tài)的節(jié)點(diǎn)拳氢;第三種也就是剩下的狀態(tài)募逞,調(diào)用compareAndSetWaitStatus方法,來看一下其實(shí)現(xiàn)
private static final boolean compareAndSetWaitStatus(Node node,int expect,int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,expect, update);
}
又見到了unsafe的CAS操作馋评,那么顯而易見第三種情況下的操作就是將上一個節(jié)點(diǎn)狀態(tài)置為SIGNAL放接。
這里可以總結(jié)一下shouldParkAfterFailedAcquire的作用了,那就是根據(jù)上一個節(jié)點(diǎn)的情況來做相應(yīng)的操作栗恩。接著就要看parkAndCheckInterrupt()方法了透乾。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
關(guān)于這個LockSupport類,這里不準(zhǔn)備再深入了磕秤,里面又是調(diào)用到了unsafe進(jìn)行一系列較為底層的操作乳乌,見但介紹一下LockSupport這個類,它是用來創(chuàng)建鎖和其他同步類的基本線程阻塞原語市咆。LockSupport 提供park()和unpark()方法實(shí)現(xiàn)阻塞線程和解除線程阻塞汉操,暫時了解到這一層就夠了。
看上去流程基本是講完了蒙兰,該總結(jié)一下非公平鎖和公平鎖的差異了磷瘤,等等,好像之前hasQueuedPredecessors()方法還晾在一邊沒講搜变。
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
總結(jié)一下采缚,這個方法做的事情是判斷等待隊(duì)列中有沒有節(jié)點(diǎn)。都到這里了挠他,是時候引出沒講的AQS了扳抽。
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer{
private transient volatile Node head;
private transient volatile Node tail;
private volatile int state;
static final class Node {
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
}
}
public abstract class AbstractOwnableSynchronizer{
private transient Thread exclusiveOwnerThread;
}
我把所有的方法和細(xì)枝末節(jié)的東西都刪了,呈現(xiàn)出來類結(jié)構(gòu)是這樣的殖侵,AQS連同它繼承的AOS類一共就只有這些屬性贸呢,重點(diǎn)是exclusiveOwnerThread屬性,這個屬性就是持有鎖的線程拢军,也就是真正的boss楞陷。整個鎖其實(shí)本質(zhì)就是圍繞兩個點(diǎn),線程和隊(duì)列茉唉,內(nèi)部類NODE有一個注釋寫的非常好固蛾,有必要放一下。
* <p>To enqueue into a CLH lock, you atomically splice it in as new
* tail. To dequeue, you just set the head field.
* <pre>
* +------+ prev +-----+ +-----+
* head | | <---- | | <---- | | tail
* +------+ +-----+ +-----+
* </pre>
這個注釋真的是高赌渣,一下子就展示出了CLH隊(duì)列的含義魏铅,這個CLH隊(duì)列中每一個節(jié)點(diǎn)就是一個NODE對象,NODE對象上記錄了對應(yīng)的排隊(duì)獲取鎖的線程坚芜,狀態(tài)用waitStatus表示览芳,規(guī)則是tail入隊(duì)head出隊(duì),當(dāng)然這里的入隊(duì)和出隊(duì)操作都是基于CAS封裝的原子操作鸿竖。分析到這里我們就可以總結(jié)出整個鎖的核心原理了沧竟,==那就是通過CLH隊(duì)列將多線程爭搶鎖的并行過程變成隊(duì)列中排隊(duì)獲取鎖的串行過程==铸敏。
最后用一張網(wǎng)上看到的畫得非常好的流程圖來區(qū)別一下公平鎖和非公平鎖的區(qū)別,區(qū)別有兩點(diǎn):==一悟泵、非公平鎖多了首次搶占杈笔;二、公平鎖在基于CLH隊(duì)列獲取鎖的過程中還要判斷隊(duì)列中是否有等待節(jié)點(diǎn)==糕非。這兩點(diǎn)也很好的展示了兩種鎖模式的特性蒙具。
NonfairSync
lock —> compareAndSetState
| —> setExclusiveOwnerThread
—> accquire
| —> tryAcquire
|—>nonfairTryAcquire
|—> acquireQueued
FairSync
lock —> acquire
| —> tryAcquire
|—>!hasQueuePredecessors
|—>compareAndSetState
|—>setExclusiveOwnerThread
|—> acquireQueued
總結(jié)
本文主要是對ReentrantLock中重點(diǎn)源碼的一個梳理和總結(jié),講解了獲取鎖的核心原理朽肥,并且對比了公平鎖和非公平鎖的區(qū)別禁筏。后續(xù)考慮逐步把concurrent包里的幾個常用的重要類都講一下,但是寫一篇分析真的太耗時間了衡招,我自己也不確定能寫幾篇篱昔,走著看吧。