在自旋分佈式鎖實現 中咱們已經分析了ReentrantLock的自旋特性,如今咱們來分析一下它的可重入特性。node
可重入特性其實說白了就是當得到鎖的線程解鎖後,從新來獲取鎖的時候會判斷本身之前是否獲取過鎖,若是獲取過就無需競爭,直接獲取。分佈式
咱們再來繼續跟進非公平鎖的加鎖代碼ui
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else //可重入的主入口,判斷是否須要參與無鎖競爭 acquire(1); }
在AbstractQueuedSynchronizer中this
public final void acquire(int arg) { //若是獲取鎖失敗且前置節點線程被喚醒 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //當前線程請求中斷 selfInterrupt(); }
//tryAcquire在非公平鎖中被重寫了,因此這裏能夠不用考慮 protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
咱們來看一下重寫後的tryAcquire()嘗試獲取方法spa
返回NonfairSync.net
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
在sync中線程
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); //獲取鎖的狀態值 int c = getState(); if (c == 0) { //若是是0,就進行無鎖競爭,競爭成功的將當前線程設爲AQS的獨佔主線程 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //若是不是0(但也可能不是1),判斷AQS的獨佔主線程是否就是當前線程 else if (current == getExclusiveOwnerThread()) { //鎖的狀態值加1,表示當前線程第幾回進入該鎖,無需參與無鎖競爭 //爲何說是非公平鎖,就是說拿到鎖的線程能夠不斷的優先獲取到鎖 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
咱們再來看一下acquireQueued()方法,返回AbstractQueuedSynchronizer中blog
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; } //由於是無限循環,若是尾部節點的前置節點要喚醒了,且當前線程被中斷了 //中斷標識被設爲true if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) //若是獲取鎖失敗,取消當前線程節點獲取鎖,喚醒前置節點線程 cancelAcquire(node); } }
private void setHead(Node node) { head = node; node.thread = null; node.prev = null; }
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //獲取尾部節點的前置節點的等待狀態 int ws = pred.waitStatus; if (ws == Node.SIGNAL) /* * 若是該狀態爲須要被喚醒狀態,返回true */ 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; }
private final boolean parkAndCheckInterrupt() { //阻塞當前線程,返回線程中斷 LockSupport.park(this); return Thread.interrupted(); }
private void cancelAcquire(Node node) { //尾部節點不能爲null 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)) { //經過無鎖競爭,將更新後的尾部節點的下一個節點設爲null compareAndSetNext(pred, predNext, null); } else { //若是node不是尾部節點了,即node在節點列表中被移除了 int ws; //更新後的尾部節點不爲頭節點且該尾部節點的等待狀態爲待喚醒狀態(不爲待喚醒狀態也會被無鎖競爭更新爲待喚醒狀態) if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { //獲取node的下一個節點 Node next = node.next; //若是該節點不爲null且該節點等待狀態不爲取消狀態 if (next != null && next.waitStatus <= 0) //經過無鎖競爭,將該節點設爲如今尾部節點的下一個節點 compareAndSetNext(pred, predNext, next); } else { //若是更新後的尾部節點的等待狀態爲取消狀態,喚醒前置節點中等待狀態不爲被取消狀態的節點 unparkSuccessor(node); } node.next = node; // help GC } }
private void unparkSuccessor(Node node) { //獲取node的等待狀態 int ws = node.waitStatus; if (ws < 0) //若是該狀態不爲取消狀態,更新爲無狀態 compareAndSetWaitStatus(node, ws, 0); //獲取node的下一個節點 Node s = node.next; //若是該節點爲null或者該節點狀態爲被取消狀態 if (s == null || s.waitStatus > 0) { //將該節點設爲null s = null; //從尾部節點開始向前遍歷 for (Node t = tail; t != null && t != node; t = t.prev) //若是遍歷的節點不爲被取消狀態,獲取該節點 if (t.waitStatus <= 0) s = t; } if (s != null) //若是該節點不爲null,喚醒該節點的線程 LockSupport.unpark(s.thread); }
static void selfInterrupt() { //當前線程嘗試中斷 Thread.currentThread().interrupt(); }
這裏有個Node的參數,AQS的內部經過Node內部類一個個鏈接起來實現FIFO同步隊列,它的各屬性以下隊列
static final class Node { /** 共享模式 */ static final Node SHARED = new Node(); /** 獨佔模式 */ static final Node EXCLUSIVE = null; /** 表示線程已被取消(等待超時或者被中斷) */ static final int CANCELLED = 1; /** 表示後繼節點中的線程須要被喚醒(unpaking) */ static final int SIGNAL = -1; /** 表示結點線程等待在condition上(等待隊列),當被signal後,會從等待隊列轉移到同步到隊列中 */ static final int CONDITION = -2; /** * 表示下一次共享模式下同步狀態會被無條件地傳播下去 */ static final int PROPAGATE = -3; /** * 當前節點的狀態 */ volatile int waitStatus; /** * 前置節點 */ volatile Node prev; /** * 後置節點 */ volatile Node next; /** * 當前節點的線程 */ volatile Thread thread; /** * 下一個等待節點 */ Node nextWaiter;
要用到的一個構造器get
Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; }
//獲取前置節點 final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; }
在AQS中包含了兩個節點屬性
/** * 頭部節點 */ private transient volatile Node head; /** * 尾部節點 */ private transient volatile Node tail;
而該參數被賦值爲
private Node addWaiter(Node mode) { //初始化一個Node節點,其線程爲當前線程,獨佔模式,這裏就是一個要添加到節點隊列的新節點 Node node = new Node(Thread.currentThread(), mode); //獲取AQS的尾部節點 Node pred = tail; if (pred != null) { //若是該節點不爲null,將尾部節點設爲新節點的前置節點 node.prev = pred; //無鎖競爭尾部節點,競爭到的節點變成節點隊列的新尾部節點 if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //若是節點隊列沒有節點,將自動建立頭尾部節點,再競爭尾部節點 enq(node); return node; }
private Node enq(final Node node) { //無限循環 for (;;) { //獲取尾部節點 Node t = tail; if (t == null) { // Must initialize //若是尾部節點爲null,表示節點隊列尚未節點,初始化一個無參構造器節點 //來參與無鎖競爭頭部節點 if (compareAndSetHead(new Node())) //競爭的新節點同時也放到尾節點,表示節點隊列只有一個節點 tail = head; } else { //若是尾部節點不爲null,表示節點隊列中有節點 //將尾部節點設爲帶有當前線程,獨佔模式的節點的前置節點 //因爲這裏是無限循環,因此這裏節點隊列中必定會有節點,沒有節點將會在前面被建立 node.prev = t; //再無鎖競爭節點隊列的尾部節點,競爭到的節點將會變成節點隊列的尾部節點 if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }