關於AQS的源碼解析,原本是沒有打算特地寫一篇文章來介紹的。不過在寫本學期課程做業中,有一門寫了關於AQS的,並且也畫了一些相關的圖,因此直接拿過來分享一下,若有錯誤歡迎指正。
而後基本簡介也都不介紹了,網上一大堆,這裏就直接進行源碼的分析了。java
AQS屬性簡介:node
屬性 | 類型 | 詳解 |
---|---|---|
Head | Node類型 | 持有鎖的線程結點,也是隊列中的頭結點 |
Tail | Node類型 | 阻塞隊列中的尾結點,同時每個新的結點進來,都插入到阻塞隊列的最後。 |
State | int類型 | 大於等於0。表明當前鎖的狀態。0表明沒有線程佔用當前鎖,大於0表明有線程持有鎖。 |
exclusiveOwnerThread(繼承自AOS) | Thread類型 | 表明獨佔鎖的線程。 |
AQS的具體結構以下圖所示:
ui
在AQS鏈表中,將每個線程包裝成Node實例,並經過鏈表的形式連接保存,在鏈式結構中,節點經過next和prev分別與前驅節點和後置節點相鏈接。其中head節點表示爲當前持有鎖的線程,不在阻塞隊列中。tail節點爲鏈表中最後一個節點,當有新的節點被添加到鏈表中後,AQS會將tail引用指向最後一個被添加進鏈表的節點。this
Node屬性簡介:線程
字段 | 簡介 | 字段 | 簡介 |
---|---|---|---|
SHARE | 標識節點當前在共享模式下 | EXCLUSIVE | 標識節點當前在獨佔模式下 |
CANCELLED | 標識當前節點所表示的線程已取消搶鎖 | SIGNAL | 標識當前節點須要在釋放鎖後喚醒後繼節點 |
CONDITION | 與ConditionObject內部類有關 | waitStatue | 取值爲以上幾種狀態 |
prev | 表明當前節點的前驅節點 | next | 表明當前節點的後繼節點 |
thread | 表明當前節點所表示的線程 |
這裏以一個鎖的具體使用方法對AQS類進行詳細的分析:
code
首先,線程先對鎖對象進行獲取操做,若是當前須要獲取的鎖對象並無其餘線程所持有,成功獲取到了鎖,將執行相關的業務代碼,執行完畢後,對鎖資源進行釋放,以便其餘線程所使用。若是當前線程獲取鎖資源失敗,說明鎖資源有其餘線程在使用,當前線程將進行阻塞狀態,等待再次獲取鎖資源。對象
以java.util.concurrent.locks.ReentrantLock.java
文件中的公平鎖爲例:blog
abstract static class Sync extends AbstractQueuedSynchronizer #java.util.concurrent.locks.ReentrantLock中第220行 static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); #調用了AQS中的方法 } ... } ================AQS==================== #java.util.concurrent.locks.AbstractQueuedSynchronizer中第1197行 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
在lock()
方法中,線程首先嚐試搶鎖tryAcquire(1)
,若是搶鎖成功則直接返回true
,表明當前線程已持有鎖資源,不然返回false
,進行下一次搶鎖動做。
當線程搶鎖失敗後,AQS將將當前線程封裝成Node
節點,並添加到阻塞隊列。以後將從阻塞隊列中依次取出等待鎖的Node
節點,並再次嘗試獲取鎖.若是再次獲取鎖失敗,則使當前線程本身中斷本身。繼承
首先獲取鎖的狀態,判斷當前是否有線程持有鎖,這裏分爲兩種狀況:隊列
若是當前並無線程持有鎖資源,則判斷阻塞隊列中是否有節點排在當前節點的前面等待獲取鎖資源。這裏分爲兩種狀況:
流程圖以下:
源碼:
#java.util.concurrent.locks.ReentrantLock中第231行 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; } } 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鏈表中的頭節點與尾節點,分別進行判斷:
流程圖以下:
源碼:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第1512行 public final boolean hasQueuedPredecessors() { Node t = tail; Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
若是當前線程搶鎖失敗則經過AQS將當前線程包裝成Node節點添加進阻塞隊列。
將當前線程以獨佔鎖的模式包裝成Node節點。
入隊操做結束將當前節點返回。
流程圖以下:
源碼:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第605行 private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
這一步將當前節點添加到阻塞隊列中。
首先獲取阻塞隊列中的尾節點,判斷是否爲空,有兩種狀況:
流程圖以下:
源碼:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第583行 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; } } } }
到達這一步說明節點已進入阻塞隊列,節點嘗試獲取鎖或者進行掛起操做。
流程圖以下:
源碼:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第857行 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)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
當線程暫時獲取不到鎖資源時,判斷是否應該掛起當前線程。
首先獲取當前節點的前驅節點的狀態,這裏有三種狀況:
* 前驅節點的狀態爲SIGNAL。其中,SIGNAL代表該節點在釋放鎖資源後應該將後置節點喚醒。返回true。
* 前驅節點的狀態爲CANCELLED。CANCELLED代表該節點已取消搶鎖,此時將從當前節點開始向前尋找,直到找到一個節點的狀態不爲CANCELLED,而後將他設置爲當前節點的前驅節點。以後返回false.
* 若是前驅節點的狀態不是以上兩種狀況,則經過CAS將前驅節點的狀態設置爲SIGNAL,以後返回false。
流程圖以下:
源碼:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第795行 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) return true; if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
將當前線程掛起,當線程被喚醒後將線程的中斷狀態返回.
源碼:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第835行 private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
嘗試釋放鎖資源,這裏有兩種狀況:
流程圖以下:
源碼:
#java.util.concurrent.locks.ReentrantLock中第456行 public void unlock() { sync.release(1); } ============================== #java.util.concurrent.locks.AbstractQueuedSynchronizer中第1260行 public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
當持有鎖的節點執行相關代碼完成後,須要釋放鎖資源並喚醒後置節點。
流程圖以下:
源碼:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第638行 private void unparkSuccessor(Node node) { int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); 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; } if (s != null) LockSupport.unpark(s.thread); }
當線程因爲異常或某些特殊狀況的發生,須要取消對鎖資源的獲取,將執行取消搶鎖操做。
流程圖以下:
源碼:
#java.util.concurrent.locks.AbstractQueuedSynchronizer中第742行 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; node.waitStatus = Node.CANCELLED; if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null); } else { int ws; if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); } else { unparkSuccessor(node); } node.next = node; // help GC } }
其實到這裏還有一些內容並無分析完,之後再補上好了。