java aqs源码_java中AQS源码分析
AQS內部采用CLH隊列。CLH隊列是由節點組成。內部的Node節點包含的狀態有
static?final?int?CANCELLED?=??1;
static?final?int?SIGNAL????=?-1;
static?final?int?CONDITION?=?-2;
static?final?int?PROPAGATE?=?-3;
其中取消狀態表示任務的取消,SIGNAL狀態表示后續節點需要喚醒,CONDITION表示等待狀態,PROPAGRATE表示的是傳播狀態通常用于共享鎖的釋放。初始節點的狀態為0。
AQS中比較重要的操作包括:
public?final?void?acquire(int?arg);
public?final?void?acquireInterruptibly(int?arg);
public?final?void?acquireShared(int?arg);
public?final?void?acquireSharedInterruptibly(int?arg);
protected?boolean?tryAcquire(int?arg);
protected?int?tryAcquireShared(int?arg);
public?final?boolean?tryAcquireNanos(int?arg,?long?nanosTimeout)?throws?InterruptedException;
public?final?boolean?tryAcquireSharedNanos(int?arg,?long?nanosTimeout)?throws?InterruptedException;
其中:public final void acquire(int arg) {
if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
其中tryAcquire方法為抽象方法。不同的子類有不同的實現方式。AQS中該方法的實現知識拋出了一個異常。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;//標記是否成功拿到資源
try {
boolean interrupted = false;//標記等待過程中是否被中斷過
//自旋的過程
for (;;) {
final Node p = node.predecessor();//拿到前驅
//如果前驅是head,即該結點已成老二,那么便有資格去嘗試獲取資源(可能是老大釋放完資源喚醒自己的,當然也可能被interrupt了)。
if (p == head && tryAcquire(arg)) {
setHead(node);//拿到資源后,將head指向該結點。所以head所指的標桿結點,就是當前獲取到資源的那個結點或null。
p.next = null; // setHead中node.prev已置為null,此處再將head.next置為null,就是為了方便GC回收以前的head結點。也就意味著之前拿完資源的結點出隊了!
failed = false;
return interrupted;//返回等待過程中是否被中斷過
}
//如果自己可以休息了,就進入waiting狀態,直到被unpark()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;//如果等待過程中被中斷過,哪怕只有那么一次,就將interrupted標記為true
}
} finally {
if (failed)
cancelAcquire(node);
}
}
可以從該方法中看出。這里會繼續嘗試去獲取一下資源
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;//拿到前驅的狀態
if (ws == Node.SIGNAL)
//如果已經告訴前驅拿完號后通知自己一下,那就可以安心休息了
return true;
if (ws > 0) {
/*
* 如果前驅放棄了,那就一直往前找,直到找到最近一個正常等待的狀態,并排在它的后邊。
* 注意:那些放棄的結點,由于被自己“加塞”到它們前邊,它們相當于形成一個無引用鏈,稍后就會被保安大叔趕走了(GC回收)!
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//如果前驅正常,那就把前驅的狀態設置成SIGNAL,告訴它拿完號后通知自己一下。有可能失敗,人家說不定剛剛釋放完呢!
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
在該方法中會檢測當前節點中前面的節點是否有CANCELLED狀態的如果有。待會兒后續的操作這些節點會被GC回收。
如果一切正常當前節點的前一個節點會被設置為SIGNAL狀態。
1 private final boolean parkAndCheckInterrupt() {
2???? LockSupport.park(this);//調用park()使線程進入waiting狀態
3???? return Thread.interrupted();//如果被喚醒,查看自己是不是被中斷的。
4 }
線程進入waiting狀態。線程被喚醒的方式有被unpark和被中斷。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;//找到頭結點
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//喚醒等待隊列里的下一個線程
return true;
}
return false;
}
這里tryRelease也是一個抽象方法不同的子類有不同的實現。
private void unparkSuccessor(Node node) 內部首先會設置當前節點的狀態為初始狀態。同時找到一個有效的后繼節點的狀態小于0的進行節點的釋放。?LockSupport.unpark(s.thread);//喚醒對應的線程。
總結
以上是生活随笔為你收集整理的java aqs源码_java中AQS源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网上代办信用卡提额靠谱/真的吗
- 下一篇: 民营银行和商业银行的区别,划重点啦!