上一講了解了 AQS 是什么唁影,接下來看看它到底是怎樣的結(jié)構(gòu)愉耙。
一. 工作原理
AQS 使用一個 volatile 的 int 類型的成員變量來表示同步狀態(tài)钞支,通過內(nèi)置的 FIFO 隊列來完成資源獲取和排隊工作,將每條要去搶占資源的線程封裝成一個 node 節(jié)點來實現(xiàn)鎖的分配,通過 CAS 來完成對 state 值的修改骤竹。
HashMap 進行 put 的時候,也不是直接存儲 key value 鍵值對往毡,而是將 key value 鍵值對封裝成 Node 節(jié)點瘤载,然后用數(shù)組 + 鏈表 + 紅黑樹存儲 Node。AQS 也類似卖擅,將要搶占資源的 Thread 封裝成 Node節(jié)點鸣奔。
二. 相關(guān)源碼:
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
static final class Node {
……
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
……
}
private transient volatile Node head;
private transient volatile Node tail;
/**
* The synchronization state.
*/
private volatile int state;
}
看到這個是不是就清清楚楚明明白白真真切切了。首先 AQS 外層是 state + CLH 隊列惩阶,state 表示同步的狀態(tài)挎狸,默認是0,為0時表示可以獲取鎖断楷,不為0時锨匆,線程就得老老實實到隊列中排隊去;CLH 隊列就是一個有頭結(jié)點和尾結(jié)點的雙端隊列冬筒,如下圖:
+------+ prev +-----+ +-----+
head | | <---- | | <---- | | tail
+------+ +-----+ +-----+
AQS 的內(nèi)層是一個 Node內(nèi)部類恐锣,這個 Node 類主要有兩個指針 prev 和 next、一個 waitStatus 表示線程的狀舞痰、土榴,一個 Thread 類型的變量保存等待的線程。
三. 從 ReentrantLock 看 AQS:
之前說了 AQS 是 JUC 并發(fā)包的基石响牛,那就從我們接觸最多的 ReentrantLock 入手玷禽,揭開它的神秘面紗。
先來看看 ReentrantLock 的結(jié)構(gòu)圖:
首先它實現(xiàn)了 Lock 接口呀打,其內(nèi)部主要是一個 Sync 內(nèi)部類矢赁,這個內(nèi)部類又有兩個子類,一個 FairSync 和一個 NonfairSync贬丛,分別用來實現(xiàn)公平鎖和非公平鎖撩银。而這個 Sync 內(nèi)部類,又是 AbstractQueuedSynchronizer 的子類豺憔。
1. 我們 new ReentrantLock 的時候做了什么事额获?
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
通過這個構(gòu)造方法可以知道,實際上是構(gòu)建了一個非公平鎖焕阿。如果 new 的時候傳了 true咪啡,調(diào)用的構(gòu)造方法就是:
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
所以傳的是 true,構(gòu)建的就是公平鎖暮屡。
2. 公平和非公平有什么區(qū)別?
非公平鎖源碼:
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 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;
}
乍一看兩段代碼好像沒啥不一樣毅桃,其實不同之處在褒纲,if (c == 0)
這段判斷中准夷。公平鎖多了一個判斷條件,即!hasQueuedPredecessors()
莺掠,看看這個方法的源碼:
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
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é)點不等于尾節(jié)點,然后就是頭節(jié)點的下一個節(jié)點為空或者頭節(jié)點的下一個節(jié)點保存的 Thread 不等于當(dāng)前的 Thread彻秆。簡單地說就是看隊列中有沒有除了當(dāng)前 Thread 以為的 Thread 在等待獲取鎖楔绞,有就返回 true,否則返回 false唇兑。所以公平鎖就是多了這個判斷酒朵,其他都一樣。
下一篇文章將會從源碼層面分析 ReentrantLock 的加鎖過程扎附,敬請期待蔫耽!