1.文章目錄
- AQS概述,類結構,源碼導讀;
- RenntractLock源碼,實現細節導讀;
2.AQS概述&類結構
AbstractQueuedSynchronizer 抽象同步隊列簡稱 AQS ,它是實現同步器的基礎組件, 併發包中鎖的底層就是使用 AQS 實現的;
- AQS 是一個FIFO 的雙向隊列,其內部通過節點 head tail 首和隊尾元素,隊列元素的類型爲Node 其中 Node 中的 thread 變量用來存放進入 AQS 隊列裏面的線程:
- Node 節點內部的 SHARED 用來標記該線程是獲取共享資源資源時被阻 掛起後放入AQS 隊列的, EXCLUSIVE 用來標記線程是獲取獨佔資源時被掛起後放入 AQS 隊列的,waitStatu 記錄當前線程等待狀態,可以爲 CANCELLED (線程被取消了)、 SIGNAL 線程需要被喚醒)、 ONDITION (線程在條件隊列裏面等待〉、 PROPAGATE 釋放共享資源時需要通知其他節點; prev 記錄當前節點的前驅節點, next 記錄當前節點的 後繼節點
- AQS 維持了的狀態信息 state,可以通過 getState setState compareAndSetState 函數修改其值,對於 ReentrantLock來說, state 可以用來表示前線程獲取鎖的可重入次數 ;對於 讀寫鎖 ReentrantReadWri teLock 來說 state16 位表示讀狀態,也就是獲取該讀鎖的次數,低16位表示獲取到寫鎖的線程的可重入次數; 對於 semaphore 來說 state 用來表示當前可用信號的 數:對於 CountDownlatch 來說,state C表示 數器當前的值;
- AQS 有個內部類ConditionObject 用來結合鎖實現線程同步 ConditionObject 可以 直接訪問 AQS 內部的變量,比如 state 態值和 AQS隊列。ConditionObject 是條 變量 個條件變量對應 件隊列 (單向鏈表隊列),其用來存放調用條件變 await 方法後被阻塞的線程,如類圖所示 這個條件隊列的頭、尾元素分別爲firstWaiter,lastWaiter;
源碼導讀
基本屬性:看上面的圖可以直到基本屬性:使用Unsafe機制,記錄變量偏移量,方便CAS操作
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
acquire(int arg):獲取獨佔資源方法
public final void acquire(int arg) {
// 首先調用tryAcquire嘗試獲取資源
if (!tryAcquire(arg) &&
,失敗則(Node.EXCLUSIVE)狀態進去等待隊列,並且LockSupport.park掛起自己
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter方法
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;
CAS插入
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果上面沒有插入成功,則循環插入
enq(node);
return node;
}
private Node enq(final Node node) {
// 循環插入
for (;;) {
Node t = tail;
// 尾部爲NULL,則head肯定也是NULl,創建比賦值
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
// 設置tail節點
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
realease(int arg):釋放資源
public final boolean release(int arg) {
// 調用tryRelease方法,主要是修改state的值
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 釋放AQS隊列一個被阻塞的線程
unparkSuccessor(h);
return true;
}
return false;
}
acquireShared(int arg)獲取共享資源
// 首先嚐試獲取資源,如果不成功,LockSupport.park當前線程,封裝Node.ShARED進入AQS隊列
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
doRelaseShared:釋放共享資源
// 嘗試釋放資源(設置state),如果成功則激活一個線程,判斷是否滿足自己的需要,不滿足再次進去隊列
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
- 到此基本的核心方法已經導讀結束,AQS作爲隊列同步的抽象,對基本方法做了實現,tryAcquire,treRelease等方法要交給子類來實現,比如可重入鎖,不可重入鎖的設計肯定不同的;後續對ReentrantLock源碼導讀會更加清晰;
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
條件變量Condition
這個變量類似於wait,notify的作用,線程同步用的,比較基礎;可以自己探索下;
3.ReentrantLock類結構&源碼導讀
- 可以看出,ReentrantLock實現了Lock接口,實現了基本接口,藉助Sync(繼承AQS)實現真正的線程同步策略;
- ReentrantLock 是可重入的獨佔鎖只能有一個線程可獲取該鎖,其他獲取該鎖的線程會被阻塞而被放入該鎖的AQS阻塞隊列裏面;
- 我們平常用這個的鎖,有公平鎖,非公平鎖(默認)兩種形式;因此內部封裝了兩種實現形式;
初始化方法:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
- 上述代碼提供了兩種初始化方式,也是我們最常用的;默認爲非公平策略,也可以根據fair參數指定;
其他核心方法:
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
public int getHoldCount() {
return sync.getHoldCount();
}
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
public boolean isLocked() {
return sync.isLocked();
}
// sync是否是FairSync的子類/類
public final boolean isFair() {
return sync instanceof FairSync;
}
protected Thread getOwner() {
return sync.getOwner();
}
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
public final int getQueueLength() {
return sync.getQueueLength();
}
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
- 可以看出都是藉助Sync(AQS)變量實現的;模板方法設計;
- 因此我們看Sync的實現
Sync類實現
- 該類繼承AQS,對AQS的方法進行了重寫,根據Reentrant策略進行設計;
- 具體的lock,tryRe
abstract static class Sync extends AbstractQueuedSynchronizer {
.........
// 交給fairSync,NoFairSync實現
abstract void lock();
}
final boolean nonfairTryAcquire(int acquires)
// 非公平策略嘗試獲取資源
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果c=0則表示沒有鎖佔有
if (c == 0) {
// CAS設置state=acquires
if (compareAndSetState(0, acquires)) {
// 設置鎖擁有線程
setExclusiveOwnerThread(current);
return true;
}
}
// 也是可重入鎖的設計特點
// 鎖被佔有了,看下是不是自己
else if (current == getExclusiveOwnerThread()) {
// 是自己,對c進行加
int nextc = c + acquires;
// 超過最大數
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
tryRelease:釋放資源
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果不是鎖擁有線程,拋出異常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果c=0了,釋放資源完了
if (c == 0) {
// free=true,鎖擁有線程爲Null
free = true;
setExclusiveOwnerThread(null);
}
// CAS設置鎖狀態
setState(c);
return free;
}
isHeldExclusively
//判斷鎖是否被自己佔有,判斷食不知自己就好了
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
幾個簡單方法:
// 生產條件變量
final ConditionObject newCondition() {
return new ConditionObject();
}
// 獲取鎖Owner
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 獲取鎖持有數
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 是否被佔有
final boolean isLocked() {
return getState() != 0;
}
- 到這幾個基本方法介紹完了,我們看如何實現公平策略,非公平策略
FairSync&NoFairSync源碼
NoFairSync類源碼
- 繼承Sync對lock,tryAcquire做了重寫;
- 不公平體現在哪:第一個調用lock方法的線程就會獲得,隨機的
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
// CAS設置state狀態
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 失敗,調用AQS的acquire,前面已經介紹,如果嘗試獲取失敗後,進入阻塞隊列
acquire(1);
}
// 服用父類Sync的方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
FairSync類源碼
- 繼承Sync對lock,tryAcquire做了重寫;
- 公平體現在:根據阻塞隊列從前向後以此獲得鎖,根據調用lock的順序進行獲得鎖;
- hasQueuedPredecessors實現了公平策略;
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 調用AQS上層方法,還是會掉tryAcquire方法
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;
}
}
//加state
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
// 如果當前線程節點有前驅節點則返回住時, 如果當前AQS隊列空或者當前線程節點是 AQS 第一個節
點則返回 false 。其中如果 h==t 說明當前隊列,直接返回 false ;
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
4.總結
- 以上是AQS,ReentrantLock的基本實現原理,對於更多細節可以自己閱讀;