AQS分析
lock.lock()過程
1.鎖的基本要素:一個共享的的數據來記錄鎖的狀態,AQS採用State作爲鎖標記,0爲無鎖,大於等於1是有鎖狀態
2.NonfairSync類代碼片段
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
(1)獲得鎖成功:
compareAndSetState是CAS->樂觀鎖,原子性
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
即將expect和stateOffset相比如果相等則更新爲update否則不更新,整個過程是原子的
compareAndSetState(0, 1)爲true則獲得鎖成功執行,即將當前線程獲得獨佔鎖:
setExclusiveOwnerThread(Thread.currentThread());
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
(2)獲得鎖失敗:
如果獲得鎖失敗則走else,acquire(1);
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
①首先分析tryAcquire(arg):
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
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;
}
首先獲取一次鎖的狀態,如果爲無鎖狀態則採用CAS將當前線程設置爲有鎖狀態,返回true
這樣好處在於,線程執行效率如果快的話,再比較一次可能更加的有效率,每一個線程都可以在這再嘗試一次是否可獲得鎖,這也是一種插隊機制。
若不爲無鎖狀態,則進行判斷是否爲重入鎖,如果是則走else if一下邏輯,此段主要是對重入鎖進行重入次數的計數並設置State的值
若以上兩段都不滿足則返回false,並且由於!tryAcquire(arg)所以走到了這一段代碼acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
②分析acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
首先分析addWaiter(Node.EXCLUSIVE) 參數說明:Node.EXCLUSIVEnull arg1
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;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
先來分析enq(node)
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
For(;;)代表死循環,以下圖爲例說明:
在第一次循環時,tail==null,所以走第一個if,然後緊接着CAS比較Head是否和null相等,如果相等則將Head設置爲一個空的Node即Node(){} ;所以如上圖step2所示,head指向了一個空節點Node,最後將tail = head。
在第二次循環時,tail已不爲空(指向空節點),走else語句:代碼如下所示
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
node爲形參傳入的node,它包裝了那個線程,例如ThreadB,以ThreadB爲例演示以下過程,首先node.prev指向t; CAS比較t跟內存中的tail是否相等,若相等則將tail更新爲node即指向node,t.next=node,返回t;請對照上圖step3看
若ThreadC進來,則不會進入enq(node)方法了,直接在以下代碼處返回
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; //此處爲ThreadC進來
if (pred != null) { //此處不爲空
node.prev = pred; //將node的prev指向上一個尾節點
if(compareAndSetTail(pred, node)){ //將尾節點換成當前節點Node(ThreadC)
pred.next = node; //pred的next指向Node(ThreadC)
return node; //返回Node(ThreadC)
}
}
enq(node);
return node;
}
此時會返回到上面的②acquireQueued(addWaiter(Node.EXCLUSIVE), arg)),則第一個Thread傳遞的爲acquireQueued(Node(ThreadB),1);現在開始分析acquireQueued:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { //首先是死循環
final Node p = node.predecessor(); //在下面進行分析1),返回prev節點
if (p == head && tryAcquire(arg)) { //若第一次p==head爲true 2)
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && //下面分析這個if 3)
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
1)node.predecessor()代碼:
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
判斷prev是否爲null,若是報錯,否則返回p
2)tryAcquire代碼:再次去嘗試獲取鎖的那段代碼
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;
}
3)shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()
1.shouldParkAfterFailedAcquire(p, node)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus; //ws默認爲0
if (ws == Node.SIGNAL) // SIGNAL爲-1
return true;
if (ws > 0) { // 大於0的只有CANCELLED==1
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //將狀態設置爲SIGNAL
}
return false;
}
2.parkAndCheckInterrupt()若想進入此方法,一定是第二個線程過來時
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); //將當前線程掛起
return Thread.interrupted(); //返回是否被interrupt過,若是則返回true,並復位,默認爲false
}
此時線程被掛起,如下圖step4所示
由下面的lock.unlock()中的unpark()時
回到這段代碼
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { //此時滿足,因爲在lock.unlock()執行unpark之後
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
tryAcquire(arg) ThreadB搶佔到鎖,並改變state和exclusiveOwnerThread爲自己,如下圖代碼所示:
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
分析這一段代碼
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
第一步:SetHead做的事情將Head指向ThreadB,將ThreadB中的thread屬性設置爲null,將指向之前的head節點設置爲指向null
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
第二步:將p的next屬性設置爲null,最終如下圖所示:
lock.unlock()過程
猜測一下,首先釋放鎖肯定會將鎖狀態設置爲0,以及線程設置爲空,其次肯定會喚醒下一個等待的節點,使得去搶佔鎖
1 sync.release(1)
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
1.1 tryRelease(arg)
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
將exclusiveOwnerThread設置爲null,將state設置爲0,即猜測第一步
1.2 繼續往下走
Node h = head;
if (h != null && h.waitStatus != 0) //滿足條件
unparkSuccessor(h);
return true;
1.3 unparkSuccessor(h)
private void unparkSuccessor(Node node) {
int ws = node.waitStatus; // ws==-1
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); //將head節點的waitstatus設置爲0
Node s = node.next; //s即爲ThreadB
if (s == null || s.waitStatus > 0) { //s不爲null
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); //走到這,將ThreadB喚醒
}
前面代碼遺留兩個問題
1
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
爲什麼要在尾部向後遍歷找到實際的,因爲可能會出現下圖情況
若從前往後找有可能會出現next沒引用上的問題
2
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
在此處被interrupt中斷後,在這裏相應:
static void selfInterrupt() {
Thread.currentThread().interrupt();
}