概述:
AbstractQueuedSynchronizer
,可以稱為抽象佇列同步器。
AQS有獨佔模式和共享模式兩種:
- 獨佔模式:
公平鎖:
非公平鎖:
- 共享模式:
資料結構:
- 基本屬性:
/** * 同步等待佇列的頭結點 */ private transient volatile Node head; /** * 同步等待佇列的尾結點 */ private transient volatile Node tail; /** * 同步資源狀態 */ private volatile int state;
- 內部類:
static final class Node { /** * 標記節點為共享模式 */ static final Node SHARED = new Node(); /** * 標記節點為獨佔模式 */ static final Node EXCLUSIVE = null; static final int CANCELLED = 1; static final int SIGNAL = -1; static final int CONDITION = -2; static final int PROPAGATE = -3; /** * CANCELLED: 值為1,表示當前的執行緒被取消 * SIGNAL: 值為-1,表示當前節點的後繼節點包含的執行緒需要執行,也就是unpark; * CONDITION: 值為-2,表示當前節點在等待condition,也就是在condition佇列中; * PROPAGATE: 值為-3,表示當前場景下後續的acquireShared能夠得以執行; * 0: 表示當前節點在sync佇列中,等待著獲取鎖。 * 表示當前節點的狀態值 */ volatile int waitStatus; /** * 前置節點 */ volatile Node prev; /** * 後繼節點 */ volatile Node next; /** * 節點同步狀態的執行緒 */ volatile Thread thread; /** * 儲存condition佇列中的後繼節點 */ 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 } 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; } }
主要方法解析:
-
tryAcquire/tryAcquireShared(int arg)
獨佔/共享模式獲取鎖;由子類實現,僅僅獲取鎖,獲取鎖失敗時不進行阻塞排隊。
-
tryRelease/tryReleaseShared(int arg)
獨佔/共享模式釋放鎖;由子類實現,僅僅釋放鎖,釋放鎖成功不對後繼節點進行喚醒操作。
-
acquire/acquireShared(int arg)
獨佔/共享模式獲取鎖,如果執行緒被中斷喚醒,會返回執行緒中斷狀態,不會拋異常中止執行操作(忽略中斷)。
-
acquireInterruptibly/acquireSharedInterruptibly(int arg)
獨佔/共享模式獲取鎖,執行緒如果被中斷喚醒,則丟擲InterruptedException異常(中斷即中止)。
-
tryAcquireNanos/tryAcquireSharedNanos(int arg, long nanosTimeout)
獨佔/共享時間中斷模式獲取鎖,執行緒如果被中斷喚醒,則丟擲InterruptedException異常(中斷即中止);如果超出等待時間則返回加鎖失敗。
-
release/releaseShared(int arg)
獨佔/共享模式釋放鎖。
-
addWaiter(Node mode)
將給定模式節點進行入隊操作。
1 private Node addWaiter(Node mode) { 2 // 根據指定模式,新建一個當前節點的物件 3 Node node = new Node(Thread.currentThread(), mode); 4 // Try the fast path of enq; backup to full enq on failure 5 Node pred = tail; 6 if (pred != null) { 7 // 將當前節點的前置節點指向之前的尾結點 8 node.prev = pred; 9 // 將當前等待的節點設定為尾結點(原子操作) 10 if (compareAndSetTail(pred, node)) { 11 // 之前尾結點的後繼節點設定為當前等待的節點 12 pred.next = node; 13 return node; 14 } 15 } 16 enq(node); 17 return node; 18 }
-
enq(final Node node)
將節點設定為尾結點。注意這裡會進行自旋操作,確保節點設定成功。因為等待的執行緒需要被喚醒操作;如果操作失敗,當前節點沒有與其他節點沒有引用指向關係,一直就不會被喚醒(除非程式程式碼中斷執行緒)。
1 private Node enq(final Node node) { 2 for (;;) { 3 Node t = tail; 4 // 判斷尾結點是否為空,尾結點初始值是為空 5 if (t == null) { // Must initialize 6 // 尾結點為空,需要初始化 7 if (compareAndSetHead(new Node())) 8 tail = head; 9 } else { 10 // 設定當前節點設定為尾結點 11 node.prev = t; 12 if (compareAndSetTail(t, node)) { 13 t.next = node; 14 return t; 15 } 16 } 17 } 18 }
-
acquireQueued(final Node node, int arg)
已經在佇列當中的節點,準備阻塞獲取鎖。在阻塞前會判斷前置節點是否為頭結點,如果為頭結點;這時會嘗試獲取下鎖(因為這時頭結點有可能會釋放鎖)。
1 final boolean acquireQueued(final Node node, int arg) { 2 boolean failed = true; 3 try { 4 boolean interrupted = false; 5 for (;;) { 6 // 當前節點的前置節點 7 final Node p = node.predecessor(); 8 // 入隊前會先判斷下該節點的前置節點是否是頭節點(此時頭結點有可能會釋放鎖);然後嘗試去搶鎖 9 // 在非公平鎖場景下有可能會搶鎖失敗,這時候會繼續往下執行 阻塞執行緒 10 if (p == head && tryAcquire(arg)) { 11 //如果搶到鎖,將頭節點後移(也就是將該節點設定為頭結點) 12 setHead(node); 13 p.next = null; // help GC 14 failed = false; 15 return interrupted; 16 } 17 // 如果前置節點不是頭結點,或者當前節點搶鎖失敗;通過shouldParkAfterFailedAcquire判斷是否應該阻塞 18 // 當前置節點的狀態為SIGNAL=-1,才可以安全被parkAndCheckInterrupt阻塞執行緒 19 if (shouldParkAfterFailedAcquire(p, node) && 20 parkAndCheckInterrupt()) 21 // 該執行緒已被中斷 22 interrupted = true; 23 } 24 } finally { 25 if (failed) 26 cancelAcquire(node); 27 } 28 }
-
shouldParkAfterFailedAcquire(Node pred, Node node)
檢查和更新未能獲取鎖節點的狀態,返回是否可以被安全阻塞。
1 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { 2 int ws = pred.waitStatus; // 獲取前置節點的狀態 3 if (ws == Node.SIGNAL) 4 /* 5 * 前置節點的狀態waitStatus為SIGNAL=-1,當前執行緒可以安全的阻塞 6 */ 7 return true; 8 if (ws > 0) { 9 /* 10 * 如果前置節點的狀態waitStatus>0,即waitStatus為CANCELLED=1(無效節點),需要從同步狀態佇列中取消等待(移除佇列) 11 */ 12 do { 13 node.prev = pred = pred.prev; 14 } while (pred.waitStatus > 0); 15 pred.next = node; 16 } else { 17 /* 18 * 將前置狀態的waitStatus修改為SIGNAL=-1,然後當前節點才可以被安全的阻塞 19 */ 20 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 21 } 22 return false; 23 }
-
parkAndCheckInterrupt()
阻塞當前節點,返回當前執行緒的中斷狀態。
1 private final boolean parkAndCheckInterrupt() { 2 LockSupport.park(this); //阻塞 3 return Thread.interrupted(); 4 }
-
cancelAcquire(Node node)
取消進行的獲取鎖操作,在非忽略中斷模式下,執行緒被中斷喚醒拋異常時會呼叫該方法。
1 // 將當前節點的狀態設定為CANCELLED,無效的節點,同時移除佇列 2 private void cancelAcquire(Node node) { 3 if (node == null) 4 return; 5 6 node.thread = null; 7 Node pred = node.prev; 8 while (pred.waitStatus > 0) 9 node.prev = pred = pred.prev; 10 11 Node predNext = pred.next; 12 node.waitStatus = Node.CANCELLED; 13 if (node == tail && compareAndSetTail(node, pred)) { 14 compareAndSetNext(pred, predNext, null); 15 } else { 16 int ws; 17 if (pred != head && 18 ((ws = pred.waitStatus) == Node.SIGNAL || 19 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && 20 pred.thread != null) { 21 Node next = node.next; 22 if (next != null && next.waitStatus <= 0) 23 compareAndSetNext(pred, predNext, next); 24 } else { 25 unparkSuccessor(node); 26 } 27 28 node.next = node; // help GC 29 } 30 }
-
hasQueuedPredecessors()
判斷當前執行緒是否應該排隊。
1.第一種結果——返回true:(1.1和1.2同時存在,1.2.1和1.2.2有一個存在)
1.1 h != t為true,說明頭結點和尾結點不相等,表示佇列中至少有兩個不同節點存在,至少有一點不為null。
1.2 ((s = h.next) == null || s.thread != Thread.currentThread())為true
1.2.1 (s = h.next) == null為true,表示頭結點之後沒有後續節點。
1.2.2 (s = h.next) == null為false,s.thread != Thread.currentThread()為true
表示頭結點之後有後續節點,但是頭節點的下一個節點不是當前執行緒
2.第二種結果——返回false,無需排隊。(2.1和2.2有一個存在)
2.1 h != t為false,即h == t;表示h和t同時為null或者h和t是同一個節點,無後續節點。
2.2 h != t為true,((s = h.next) == null || s.thread != Thread.currentThread())為false
表示佇列中至少有兩個不同節點存在,同時持有鎖的執行緒為當前執行緒。
1 public final boolean hasQueuedPredecessors() { 2 Node t = tail; // Read fields in reverse initialization order 3 Node h = head; 4 Node s; 5 return h != t && 6 ((s = h.next) == null || s.thread != Thread.currentThread()); 7 }