前面我們學習了ReentrantLock,其底層就是用了AQS實現的,應該先講這一章節,但是當時給忘了,現在給補上吧。
關於ReentrantLock的學習,可以參考:https://www.jianshu.com/p/edec5185196d
AbstractQueuedSynchronizer是阻塞式鎖,以及同步器組件的實現框架。是JDK中實現併發編程的核心,它提供了一個基於FIFO隊列,平時我們工作中經常用到的ReentrantLock,CountDownLatch等都是基於它來實現的。
一、初識AQS
首先我們還是從前面學習的ReentrantLock入手,看看其代碼結構是什麼樣的:
從上圖可以看到以下幾點:
- ReentrantLock實現接口Lock(抽象接口)
- ReentrantLock有三個內部類,分別是FrairSync、NonfairSync、Sync,且FrairSync、NonfairSync繼承自Sync。
- Sync繼承AbstractQueuedSynchronizer
- AbstractQueuedSynchronizer有兩個內部類,分別是Node、ConditionObject。
- AbstractQueuedSynchronizer繼承自AbstractOwnableSynchronizer(抽象類,提供獨佔線程的get和set)。
AQS有如下的特點:
-
用 state 屬性來表示資源的狀態,包含獨佔狀態和共享狀態,對應公平鎖和非公平鎖。子類需要定義如何維護這個狀態,控制如何獲取鎖和釋放鎖,如上面圖中的關係,公平鎖和非公平鎖需要各自去維護這個state,達到獲取和釋放鎖的目的。
- getState - 獲取 state 狀態
- setState - 設置 state 狀態
- compareAndSetState - 使用CAS設置狀態
- 獨佔模式:只有一個線程能夠訪問資源
- 共享模式:允許多個線程訪問資源
提供了基於 FIFO 的等待隊列,類似於前面講Synchronized原理提到的 Monitor 的 EntryList
使用條件變量來實現等待隊列、線程喚醒機制,同時支持多個條件變量,類似於前面講Synchronized原理提到的 Monitor 的 WaitSet
【公平鎖】與【非公平鎖】:二者的區別主要在於獲取鎖是否和排隊順序有關。當鎖唄一個線程持有,其他嘗試獲取鎖的線程會被掛起,加到等待隊列中,先被掛起的在隊列的最前端。當鎖被釋放,需要通知隊列中的線程。作爲公平鎖,會先喚醒隊列最前端的線程;而非公平鎖會喚醒所有線程,通過競爭去獲取鎖,後來的線程有可能獲得鎖。
二、 源碼分析
下面我們通過源碼剖析其本質是什麼樣的。
首先在腦海中有個印象,AQS維護了兩個對個隊列,一個是同步隊列,一個是阻塞隊列。
Node可以說是【同步隊列】和【阻塞隊列】的節點。
2.1 Node源碼剖析
static final class Node {
// 模式,分爲共享與獨佔
// 共享模式
static final Node SHARED = new Node();
// 獨佔模式
static final Node EXCLUSIVE = null;
// 結點狀態
// CANCELLED,值爲1,表示當前的線程被取消
// SIGNAL,值爲-1,表示當前節點的後繼節點包含的線程需要運行,也就是unpark
// CONDITION,值爲-2,表示當前節點在等待condition,也就是在condition隊列中
// PROPAGATE,值爲-3,表示當前場景下後續的acquireShared能夠得以執行
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;
// 結點是否在共享模式下等待
final boolean isShared() {
return nextWaiter == SHARED;
}
// 獲取前驅結點,若前驅結點爲空,拋出異常
final Node predecessor() throws NullPointerException {
// 保存前驅結點
Node p = prev;
if (p == null) // 前驅結點爲空,拋出異常
throw new NullPointerException();
else // 前驅結點不爲空,返回
return p;
}
// 無參構造函數
Node() { // Used to establish initial head or SHARED marker
}
// 構造函數,被addWaiter使用
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
// 構造函數
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
2.2 ConditionObject源碼剖析
實現了condition接口,關於condition的學習後面會介紹,在學習ReentrantLock時也介紹了其使用方式。
代碼較多,直接從上向下看吧:
// 內部類
public class ConditionObject implements Condition, java.io.Serializable {
// 版本號
private static final long serialVersionUID = 1173984872572414699L;
// condition隊列的頭結點
private transient Node firstWaiter;
// condition隊列的尾結點
private transient Node lastWaiter;
/**
* 構造函數
*/
public ConditionObject() { }
/**
* 添加新的waiter到wait隊列
*/
private Node addConditionWaiter() {
// 定義尾結點是t
Node t = lastWaiter;
// 尾結點不爲空,並且尾結點的狀態不爲CONDITION(默認是-2,表示當前節點在conditionObject等待隊列中)
if (t != null && t.waitStatus != Node.CONDITION) {
// 清除狀態不爲CONDITION的結點,對firstWaiter和lastWaiter重新賦值
unlinkCancelledWaiters();
// 將最後一個結點重新賦值給t
t = lastWaiter;
}
// 新建一個結點
Node node = new Node(Thread.currentThread(), Node.CONDITION);
// 尾結點爲空
if (t == null)
// 設置condition隊列的頭結點
firstWaiter = node;
else
// 設置爲節點的nextWaiter域爲node結點
t.nextWaiter = node;
// 更新condition隊列的尾結點
lastWaiter = node;
return node;
}
/**
* 移除或轉移頭結點到sync隊列,直到沒有取消的或者空爲止
*/
private void doSignal(Node first) {
// 循環
do {
// 將下一個節點設爲首節點,如果爲空
if ( (firstWaiter = first.nextWaiter) == null)
// 設置尾結點爲空
lastWaiter = null;
// 設置first結點的nextWaiter域
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null); // 將結點從condition隊列轉移到sync隊列失敗並且condition隊列中的頭結點不爲空,一直循環
}
/**
* 轉移所有等待隊列的節點到同步隊列
*/
private void doSignalAll(Node first) {
// condition隊列的頭結點尾結點都設置爲空
lastWaiter = firstWaiter = null;
// 循環
do {
// 獲取first結點的nextWaiter域結點
Node next = first.nextWaiter;
// 設置first結點的nextWaiter域爲空
first.nextWaiter = null;
// 將first結點從condition隊列轉移到sync隊列
transferForSignal(first);
// 重新設置first
first = next;
} while (first != null);
}
/**
* 過濾掉狀態不爲CONDITION的節點
* 對firstWaiter和lastWaiter重新賦值
**/
private void unlinkCancelledWaiters() {
// 獲取condition隊列頭結點
Node t = firstWaiter;
// 獲取一個尾結點是null
Node trail = null;
while (t != null) {
// 獲取下一個結點
Node next = t.nextWaiter;
// 頭結點的狀態不爲CONDTION狀態
if (t.waitStatus != Node.CONDITION) {
// 設置t節點的下一個等待者爲空
t.nextWaiter = null;
if (trail == null) // trail爲空,首次進來一定爲空
// 重新設置condition隊列的頭結點
firstWaiter = next;
else
// 設置trail結點的nextWaiter域爲next結點
trail.nextWaiter = next;
if (next == null) // next結點爲空
// 設置condition隊列的尾結點
lastWaiter = trail;
}
else // t結點的狀態爲CONDTION狀態
// 設置trail結點
trail = t;
// 設置t結點
t = next;
}
}
/**
* 實現Condition接口的signal方法
*/
public final void signal() {
if (!isHeldExclusively()) // 不被當前線程獨佔,拋出異常
throw new IllegalMonitorStateException();
// 保存condition隊列頭結點
Node first = firstWaiter;
if (first != null) // 頭結點不爲空
// 喚醒一個等待線程,將頭結點從阻塞隊列移除,添加到同步隊列
doSignal(first);
}
/**
* 實現Condition的signalAll方法,喚醒所有線程
*/
public final void signalAll() {
if (!isHeldExclusively()) // 不被當前線程獨佔,拋出異常
throw new IllegalMonitorStateException();
// 保存condition隊列頭結點
Node first = firstWaiter;
if (first != null) // 頭結點不爲空
// 喚醒所有等待線程,將頭結點從阻塞隊列移除,添加到同步隊列
doSignalAll(first);
}
/**
* 與await()區別在於,使用await方法,調用interrupt()中斷後會報錯,而該方法不會報錯。
*/
public final void awaitUninterruptibly() {
// 添加一個結點到等待隊列
Node node = addConditionWaiter();
// 獲取釋放的狀態
int savedState = fullyRelease(node);
boolean interrupted = false;
while (!isOnSyncQueue(node)) { //
// 阻塞當前線程
LockSupport.park(this);
if (Thread.interrupted()) // 當前線程被中斷
// 設置interrupted狀態
interrupted = true;
}
if (acquireQueued(node, savedState) || interrupted) //
selfInterrupt();
}
/**
* 等待,當前線程在接到信號或被中斷之前一直處於等待狀態
*/
public final void await() throws InterruptedException {
// 當前線程被中斷,拋出異常
if (Thread.interrupted())
throw new InterruptedException();
// 將當前線程包裝成Node,尾插入到等待隊列中
Node node = addConditionWaiter();
// 釋放當前線程所佔用的lock,在釋放的過程中會喚醒同步隊列中的下一個節點
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
// 當前線程進入到等待狀態
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) // 檢查結點等待時的中斷類型
break;
}
// 自旋等待獲取到同步狀態(即獲取到lock)
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
// 處理被中斷的情況
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
/**
* 等待,當前線程在接到信號、被中斷或到達指定等待時間之前一直處於等待狀態
*/
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return deadline - System.nanoTime();
}
/**
* 等待,當前線程在接到信號、被中斷或到達指定最後期限之前一直處於等待狀態
*/
public final boolean awaitUntil(Date deadline)
throws InterruptedException {
long abstime = deadline.getTime();
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (System.currentTimeMillis() > abstime) {
timedout = transferAfterCancelledWait(node);
break;
}
LockSupport.parkUntil(this, abstime);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
/**
* 等待,當前線程在接到信號、被中斷或到達指定等待時間之前一直處於等待狀態。此方法在行爲上等
* 效於:awaitNanos(unit.toNanos(time)) > 0
*/
public final boolean await(long time, TimeUnit unit)
throws InterruptedException {
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
// 1. 將當前線程包裝成Node,尾插入到等待隊列中
Node node = addConditionWaiter();
// 2. 釋放當前線程所佔用的lock,在釋放的過程中會喚醒同步隊列中的下一個節點
int savedState = fullyRelease(node);
final long deadline = System.nanoTime() + nanosTimeout;
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
timedout = transferAfterCancelledWait(node);
break;
}
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
2.3 鎖的獲取和釋放
整個AQS的設計理念就是通過state字段來實現鎖的獲取和釋放,鎖有主要分爲公平鎖和非公平鎖。
2.3.1 公平鎖
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
// 繼承自AQS的方法,內部先調用tryAcquire獲取鎖,獲取失敗則添加下城到等待隊列當中
acquire(1);
}
/**
* 公平鎖版本的tryAcquire
*/
protected final boolean tryAcquire(int acquires) {
// 獲取當前線程
final Thread current = Thread.currentThread();
// 獲取鎖的狀態
int c = getState();
// 0表示鎖沒有被持有
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;
}
}
2.3.2 非公平鎖
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 立即獲取鎖,失敗會加入等待隊列
*/
final void lock() {
// 通過CAS嘗試獲取鎖,成功則設置當前線程獨佔
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 否則加入等待隊列·····································································································································
acquire(1);
}
/**
* 非公平鎖版本的tryAcquire
*/
protected final boolean tryAcquire(int acquires) {
// 走其父類Sync的默認nonfairTryAcquire
return nonfairTryAcquire(acquires);
}
}
2.3.3 Syc子類
這是公平鎖和非公平鎖的父類,提供統一的tryRelease方法釋放鎖
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 提供非公平版本的快捷路徑
*/
abstract void lock();
/**
* 非公平鎖獲取,默認就是非公平鎖
*/
final boolean nonfairTryAcquire(int acquires) {
// 獲取當前線程
final Thread current = Thread.currentThread();
// 獲取當前鎖的狀態
int c = getState();
// 0表示沒有被佔用
if (c == 0) {
// CAS佔用,成功則設置當前線程爲獨佔鎖
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 當前線程是獨佔鎖,表示鎖重入
else if (current == getExclusiveOwnerThread()) {
// 狀態 + 1
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;
// 當全部釋放後,狀態爲0,取消獨佔線程
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 設置狀態爲0,返回釋放成功
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
// 當前線程是否是鎖持有者
return getExclusiveOwnerThread() == Thread.currentThread();
}
// 獲取當前持有者
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 獲取持有數,只有是線程持有者才能獲取
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
}
3.3.4 acquire 和 release
在AQS當中還有兩個核心方法:
- acquire():獲取鎖,這是實際鎖調用上鎖的真正方法,前面的try開頭的知識嘗試獲取鎖,即使失敗也不會添加到等待隊列。
public final void acquire(int arg) {
// 嘗試獲取
if (!tryAcquire(arg) &&
// 嘗試獲取成功,以獨佔方式添加到等待隊列,並不斷地嘗試佔有鎖知道成功
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- release():釋放鎖,這是實際釋放鎖的方法,會調用鎖自定義的同步器實現的tryRelease方法:
/** * 嘗試釋放,成功後返回true */ public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
後面我們自定義不可重入鎖,來看看同步器和鎖的關係是什麼樣的,加深理解。
2.4 簡單總結
到此爲止,通過閱讀前面的源碼內容,我們可以有如下的總結:
- 鎖的釋放和獲取都是圍繞 【state】來做的,0表示未持有鎖,1表示獨佔,大於一表示鎖重入
- 獲取鎖的姿勢如下:
// 如果獲取鎖失敗 if (!tryAcquire(arg)) { // 入隊, 可以選擇阻塞當前線程 park unpark }
- 釋放鎖的姿勢如下:
// 如果獲取鎖成功 if (!tryRelease(arg)) { // 讓阻塞線程恢復運行 }
三、實踐
瞭解了AQS的結構之後,我們不妨自己動手實踐一番。加深理解。
定義一個不可重入鎖,需要一個同步器,一個鎖,一個測試類
自定義同步器:
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* @description: 實現一個不可重入鎖 同步器,state最大隻能是1
* @author:weirx
* @date:2022/1/13 13:49
* @version:3.0
*/
public class MyLockSynchronizer extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int acquires) {
int state = getState();
if (state == 0) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
}
return false;
}
@Override
protected boolean tryRelease(int acquires) {
int c = getState() - acquires;
if (c == 0) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
return false;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
protected ConditionObject newCondition() {
return new ConditionObject();
}
}
自定義鎖:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* @description: 自定義鎖
* @author:weirx
* @date:2022/1/13 14:05
* @version:3.0
*/
public class MyLock implements Lock {
MyLockSynchronizer myLockSynchronizer = new MyLockSynchronizer();
@Override
public void lock() {
// 嘗試獲取鎖,失敗則加入等待隊列
myLockSynchronizer.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
// 嘗試獲取鎖,失敗則加入等待隊列, 可中斷
myLockSynchronizer.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
// 嘗試獲取鎖,不加入等待隊列
return myLockSynchronizer.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
// 嘗試獲取鎖,不加入等待隊列,有實現
return myLockSynchronizer.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
// 釋放鎖
myLockSynchronizer.release(1);
}
@Override
public Condition newCondition() {
// 條件變量
return myLockSynchronizer.newCondition();
}
}
測試鎖的效果:
/**
* @description: 測試
* @author:weirx
* @date:2022/1/13 14:24
* @version:3.0
*/
public class TestMyLock {
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(() -> {
try {
myLock.lock();
System.out.println(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
+ " " + Thread.currentThread().getName() + " :acquire lock success");
// 休眠一秒看效果
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
myLock.unlock();
System.out.println(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
+ " " + Thread.currentThread().getName() + " :release lock success");
}
}, "t1").start();
new Thread(() -> {
try {
myLock.lock();
System.out.println(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
+ " " + Thread.currentThread().getName() + " :acquire lock success");
} finally {
myLock.unlock();
System.out.println(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
+ " " + Thread.currentThread().getName() + " :release lock success");
}
}, "t2").start();
}
}
結果,ti一秒後釋放鎖,纔會由t2獲得鎖:
2022-01-13 14:34:56 t1 :acquire lock success
2022-01-13 14:34:57 t2 :acquire lock success
2022-01-13 14:34:57 t1 :release lock success
2022-01-13 14:34:57 t2 :release lock success
測試下不可重入是否好使,只需要在上述測試代碼的線程t1中,再次使用myLock.lock()獲取一次鎖,發現,整個程序被卡住了,只會打印一條信息:
2022-01-13 14:35:56 t1 :acquire lock success
四、關於Condition的補充
本篇沒有介紹Condition的具體內容,但是在之前講解ReentrantLock提到過【條件變量】,可以返回去看這篇文章瞭解其用法:https://www.jianshu.com/p/edec5185196d
源碼學習真是難,看別人說的再多不如自己跟着走一遍,建議同學們參照本文自己跟蹤一遍。