簡介
AbstractQueuedSynchronizer,同步器,以下簡稱AQS。本文從源碼分析AQS的核心方法和實現原理。
AQS內部有兩組重要的成員變量:
- int類型的status變量,通過CAS操作(詳見:CAS深度分析)改變status值來控制當前線程能否訪問資源以及併發數量。
- Node類型的head和tail兩個變量,兩個變量維護了一個FIFO的同步隊列,將獲取訪問權限失敗的線程構造成Node節點加入隊列中,釋放資源時再來喚醒隊列中阻塞的線程。(Node類型主要包涵節點的狀態,當前線程的引用,以及前驅節點和後置節點的引用)
使用方式
AQS在使用時一般是作爲自定義同步工具的內部類,實現AQS中可重寫的方法,來自定義獲取以及釋放鎖的方式,在自定義同步工具類中,調用AQS中提供給使用者的模版方法,來控制鎖的獲取和釋放。
提供給使用者調用的模版方法:
獨佔式
void acquire(int arg)
以獨佔模式獲取對象,忽略中斷。
void acquireInterruptibly(int arg) throws InterruptedException
以獨佔模式獲取對象,如果被中斷則中止,拋出InterruptedException。
boolean tryAcquireNanos(int arg,long nanosTimeout) throws InterruptedException
試圖以獨佔模式獲取對象,如果被中斷則中止,拋出InterruptedException,如果到了給定超時時間,則會返回失敗。
boolean release(int arg)
以獨佔模式釋放對象。
共享式
void acquireShared(int arg)
以共享模式獲取對象,忽略中斷。
void acquireSharedInterruptibly(int arg) throws InterruptedException
以共享模式獲取對象,如果被中斷則中止,拋出InterruptedException。
boolean tryAcquireSharedNanos(int arg,long nanosTimeout) throws InterruptedException
試圖以共享模式獲取對象,如果被中斷則中止,拋出InterruptedException,如果到了給定超時時間,則會返回失敗。
boolean releaseShared(int arg)
以共享模式釋放對象。
同步器可重寫的方法:
獨佔式
protected boolean tryAcquire(int arg)
獨佔的獲取這個狀態。這個方法的實現需要查詢當前狀態是否允許獲取,然後再進行獲取(使用compareAndSetState來做)狀態。
protected boolean tryRelease(int arg)
釋放狀態。
共享式
protected int tryAcquireShared(int arg)
共享的模式下獲取狀態。
protected boolean tryReleaseShared(int arg)
共享的模式下釋放狀態。
核心方法及源碼分析
獨佔模式的獲取和釋放
* void acquire(int arg)
以獨佔模式獲取對象,忽略中斷。
public final void acquire(int arg) {
if (!tryAcquire(arg) //通過CAS更新status嘗試獲取
&& acquireQueued(addWaiter(Node.EXCLUSIVE) //獲取鎖失敗後添加到同步隊列
, arg)) //在隊列中自旋直至成功獲取鎖才返回,線程可能被反覆park、unpark,直到獲取鎖,返回值代表是否被中斷過
selfInterrupt(); //之前被中斷過則還原中斷狀態
}
private Node addWaiter(Node mode) { //添加到同步隊列
Node node = new Node(Thread.currentThread(), mode); //把當前線程構造成Node節點
Node pred = tail; //獲取當前的tail節點
if (pred != null) {
node.prev = pred; //當前節點前驅節點設置爲當前的tail節點
if (compareAndSetTail(pred, node)) { //CAS操作,如果內存中當前tail節點的值是pred,則將tail節點指向當前新節點node,返回true代表新節點成功進入隊列
pred.next = node; //將前驅節點的next設置爲node節點,這個節點無法和tail節點一起通過CAS操作設置,next節點僅僅是爲了優化,當next爲空時,始終可以通過tail節點的pred字段從後向前遍歷所有節點。
return node;
}
}
enq(node); //失敗時通過無線循環直至成功添加節點
return node;
}
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;
}
}
}
}
final boolean acquireQueued(final Node node, int arg) { //使節點阻塞自旋,直至獲取到鎖,才返回。
boolean failed = true; //當前獲取是否失敗
try {
boolean interrupted = false; //當前獲取是否被中斷
for (;;) {
final Node p = node.predecessor(); //獲取有效的前驅節點,前驅節點一定不爲null
if (p == head //head節點要麼是初始化的節點,要麼代表當前成功獲取到鎖的線程,所以前驅節點是head的時候,當前節點才應該嘗試去獲取鎖
&& tryAcquire(arg) ) { //嘗試獲取鎖
setHead(node); //設置當前節點爲head
p.next = null; //之前老的head節點next引用置空 help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) //查看當前節點是否應該被park
&& parkAndCheckInterrupt()) //park當前線程,直到被其他線程unpark,如果被中斷喚醒,則返回true,由於acquire忽略中斷所以重新嘗試獲取鎖,獲取失敗失敗重新park。
interrupted = true; //線程被中斷過
}
} finally {
if (failed)
cancelAcquire(node); //取消當前線程
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //查看當前節點是否應該被park
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) //前驅節點是signal狀態, park當前線程
return true;
if (ws > 0) { //前驅結點已取消
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0); //前驅結點已取消,向前遍歷直到找到一個非取消結點,同時將取消節點的前後節點相連
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //將當前節點設置爲signal狀態,表明後繼節點需要unpark
}
return false;
}
private final boolean parkAndCheckInterrupt() { //park當前線程,並返回當前中斷狀態
LockSupport.park(this); //掛起當前線程,當中斷或者被其他線程unpark這個線程則返回,不區分park和unpark的先後
return Thread.interrupted(); //清空並返回中斷狀態,保證後續仍然可以park,返回的值將決定完成獲取後是否需要恢復中斷狀態
}
private void cancelAcquire(Node node) { //取消當前節點
if (node == null)
return;
node.thread = null;
Node pred = node.prev;
while (pred.waitStatus > 0) //跳過已經取消的前驅節點
node.prev = pred = pred.prev;
Node predNext = pred.next; //predNext是需要移除的結點
node.waitStatus = Node.CANCELLED; //無條件設置節點狀態爲取消
if (node == tail && compareAndSetTail(node, pred)) { //如果處於鏈尾,直接移除,再修復前驅的連接關係
compareAndSetNext(pred, predNext, null);
} else {node有後繼。用前驅的next指針指向他,這樣他會得到正確的signal信號,否則喚醒他來傳播信號。
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) { //如果true,說明node有一個需要signal的前驅,讓這個前驅指向node的後繼,完整節點的鏈接關係
Node next = node.next;
if (next != null && next.waitStatus <= 0) //node有後繼
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node); //在執行到上面一句將waitStatus置CANCELLED之前,鎖被釋放,該node線程被喚醒,則釋放鎖線程的unparkSuccessor不能起到預期作用,所以這裏需要調用unparkSuccessor.即使此時持有鎖的線程沒有釋放鎖也不會有嚴重後果,被unpark的線程在獲取鎖失敗後會繼續park
}
node.next = node; // help GC
}
}
* void acquireInterruptibly(int arg) throws InterruptedException
以獨佔模式獲取對象,如果被中斷則中止,拋出InterruptedException。
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted()) //被中斷則清空中斷狀態並拋出異常
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
throws InterruptedException { //和acquireQueued的區別就是被中斷則不再重新獲取,直接結束
final Node node = addWaiter(Node.EXCLUSIVE); //將節點添加進同步隊列
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) //park當前線程,直到被其他線程unpark,如果被中斷喚醒,則返回true
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
* boolean tryAcquireNanos(int arg,long nanosTimeout) throws InterruptedException
試圖以獨佔模式獲取對象,如果被中斷則中止,拋出InterruptedException,如果到了給定超時時間,則會返回失敗。
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted()) //被中斷則清空中斷狀態並拋出異常
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
private boolean doAcquireNanos(int arg, long nanosTimeout) //doAcquireInterruptibly
throws InterruptedException {
long lastTime = System.nanoTime(); //獲取當前時間
final Node node = addWaiter(Node.EXCLUSIVE); //將節點添加進同步隊列
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
if (nanosTimeout <= 0) //如果沒有獲取到,且超時時間小於等於0則返回失敗
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold) //spinForTimeoutThreshold爲1000納秒,當休眠時間小於等於1000納秒時,由於非常短的超時等待無法做到十分精確,也會帶來額外的線程上下文切換,所以這裏直接進入快速的自旋。否則park線程
LockSupport.parkNanos(this, nanosTimeout);
long now = System.nanoTime(); //獲取unpark後的時間
nanosTimeout -= now - lastTime; //將超時時間減去獲取鎖以及線程park的時間,得到之後還需要park的時間。
lastTime = now;
if (Thread.interrupted()) //被中斷則清空中斷狀態並拋出異常
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
* boolean release(int arg)
以獨佔模式釋放對象。
public final boolean release(int arg) {
if (tryRelease(arg)) { //如果修改status狀態釋放鎖成功
Node h = head; //head是初始化的節點或代表當前佔有鎖的線程,所以要unparkhead的有效後繼節點
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); //unparkhead節點的有效後繼節點
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0) //重置頭節點的狀態爲0
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) { //後繼節點是null或者已取消
s = null;
for (Node t = tail; t != null && t != node; t = t.prev) //節點的next字段用於優化,當next鏈接沒有時,仍然需要從tail節點向前遍歷檢查,獲取隊列最前面的有效節點作爲需要unpark的節點
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//unpark同步隊列最前面的有效節點
}
共享模式的獲取和釋放
* void acquireShared(int arg)
以共享模式獲取對象,忽略中斷。
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0) //tryAcquireShared大於等於0則共享鎖獲取成功
doAcquireShared(arg);
}
private void doAcquireShared(int arg) { //和獨佔式的acquireQueued方法區別就是獲取成功的節點會繼續unpark後繼節點,將共享狀態向後傳播
final Node node = addWaiter(Node.SHARED); //添加到同步隊列
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) { //節點的前驅是head
int r = tryAcquireShared(arg);
if (r >= 0) { //獲取鎖成功
setHeadAndPropagate(node, r); //設置頭節點並unpark後繼節點,傳播共享狀態
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHeadAndPropagate(Node node, int propagate) { //設置頭節點並unpark後繼節點,傳播共享狀態。參數propagate代表當前還有可以獲取的鎖數量
Node h = head;
setHead(node); //設置當前節點爲head節點
if (propagate > 0 || h == null || h.waitStatus < 0) {//當前有資源可以獲取,或當前節點可以喚醒後繼節點
Node s = node.next;
if (s == null || s.isShared()) //後繼節點是共享的則unpark後繼節點
doReleaseShared(); //unpark後繼節點
}
}
* void acquireSharedInterruptibly(int arg) throws InterruptedException
以共享模式獲取對象,如果被中斷則中止,拋出InterruptedException。
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
* boolean tryAcquireSharedNanos(int arg,long nanosTimeout) throws InterruptedException
試圖以共享模式獲取對象,如果被中斷則中止,拋出InterruptedException,如果到了給定超時時間,則會返回失敗。
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
long lastTime = System.nanoTime();
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return true;
}
}
if (nanosTimeout <= 0)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
long now = System.nanoTime();
nanosTimeout -= now - lastTime;
lastTime = now;
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
* boolean releaseShared(int arg)
以共享模式釋放對象。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared(); //unpark後繼節點,傳播共享狀態
return true;
}
return false;
}
private void doReleaseShared() { //unpark後繼節點,傳播共享狀態
for (;;) {
Node h = head;
if (h != null && h != tail) { //如果隊列中有節點
int ws = h.waitStatus;
if (ws == Node.SIGNAL) { //頭節點狀態是signal,則重置狀態爲0,並unpark後繼節點
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) //如果狀態爲0,則將0設置爲PROPAGATE狀態
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}