一 UML類圖
通過類圖ReentrantLock是同步鎖,同一時間只能有一個執行緒獲取到鎖,其他獲取該鎖的執行緒會被阻塞而被放入AQS阻塞佇列中。ReentrantLock類繼承Lock介面;內部抽象類Sync實現抽象佇列同步器AbstractQueuedSynchronizer;Sync類有兩個子類NonfairSync(非公平鎖)和FairSync(公平鎖),預設構造方法使用非公平鎖,可以使用帶布林引數的構造方法指定使用公平鎖;ReentrantLock可以建立多個條件進行繫結。
二 原理分析
2.1 獲取鎖
2.1.1 void lock()方法
呼叫執行緒T呼叫該方法嘗試獲取當前鎖。
①如果鎖未被其他執行緒獲取,則呼叫執行緒T成功獲取到當前鎖,然後設定當前鎖的擁有者為呼叫執行緒T,並設定AQS的狀態值state為1,然後直接返回。
②如果呼叫執行緒T之前已經獲取當前鎖,則只設定設定AQS的狀態值state加1,然後返回。
③如果當前鎖已被其他執行緒獲取,則呼叫執行緒T放入AQS佇列後阻塞掛起。
AQS佇列中節點的waitStatus列舉值(java.util.concurrent.locks.AbstractQueuedSynchronizer.Node)含義:
static final int CANCELLED = 1; //執行緒被取消
static final int SIGNAL = -1; //成功的執行緒需要被喚醒
static final int CONDITION = -2; //執行緒在條件佇列中等待
static final int PROPAGATE = -3; //釋放鎖是需要通知其他節點
public void lock() { sync.lock();//委託內部公平鎖和非公平鎖獲取鎖 }
//非公平鎖
final void lock() { if (compareAndSetState(0, 1))//設定AQS狀態值為1 setExclusiveOwnerThread(Thread.currentThread());//成功設定鎖的執行緒擁有者 else acquire(1);//失敗加入到AQS佇列阻塞掛起 } //公平鎖 final void lock() { acquire(1); }
非公平鎖分析:
//呼叫父類AbstractOwnableSynchronizer方法CAS設定state值,成功返回true,失敗返回false protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } //呼叫父類AbstractOwnableSynchronizer方法,設定當前執行緒為鎖的擁有者 protected final void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread; }
//呼叫AbstractQueuedSynchronizer父類方法,第一次獲取鎖失敗 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//排它鎖型別 selfInterrupt(); } //NonfairSync子類,重寫父類方法 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) {//二次獲取鎖 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) {//當前執行緒已獲取鎖,AQS狀態值加1 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
//AbstractQueuedSynchronizer類建立節點,新增到AQS佇列後面 private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode);//建立AQS佇列的節點,節點型別排它鎖 Node pred = tail;//尾結點 if (pred != null) { node.prev = pred;//新節點的前一個節點是尾結點 if (compareAndSetTail(pred, node)) {//CAS機制新增節點 pred.next = node;//尾結點執行新的節點 return node; } } enq(node); return node; }
//插入節點到佇列中
private Node enq(final Node node) { for (;;) { Node t = tail;//尾結點 if (t == null) { // 尾結點為空,初始化 if (compareAndSetHead(new Node()))//第一步:CAS建立頭結點(哨兵節點)一個空節點 tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) {//第二步:CAS設定尾結點 t.next = node; return t; } } } }
// 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)) {//如果p節點的前節點是頭結點,並且嘗試給鎖枷鎖 setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node);//失敗解鎖 } } //阻塞掛起當前執行緒 static void selfInterrupt() { Thread.currentThread().interrupt(); }
//
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; if (ws > 0) { /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */ do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } // private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
公平鎖分析:
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; }
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s;
//①h != t:表示AQS佇列頭結點和尾結點不相同,佇列不為空;
//②(s = h.next) == null || s.thread != Thread.currentThread():頭結點(哨兵節點)為空或者next節點不等於當前執行緒 return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
2.1.2 void lockInterruptibly()方法
與2.2.1方法相似,不同之處在於:該方法對中斷進行響應,其他執行緒呼叫當前執行緒中斷方法,丟擲InterruptedException。
2.1.3 boolean tryLock()方法
嘗試獲取鎖。注意:該方法不會引起當前執行緒阻塞。
2.1.4 boolean tryLock(long timeout, TimeUnit unit)方法
與2.1.3方法相似,不同之處在於:設定了超時時間。
2.2 釋放鎖
嘗試釋放鎖。
如果當前執行緒T已獲取鎖,則呼叫該方法更新AQS狀態值減1。如果當前狀態值為0,則釋放鎖;如果當前狀態值部位0,則只是減1操作。
如果當前執行緒T未獲取鎖,則呼叫該方法是會丟擲IllegalMonitorStateException異常。
2.2.1 void unlock()方法
public void unlock() { sync.release(1); } //呼叫AbstractQueuedSynchronizer方法 public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h);//喚醒執行緒 return true; } return false; } //回撥Sync類釋放鎖 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; } // private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus;//執行緒阻塞等待狀態 if (ws < 0) compareAndSetWaitStatus(node, ws, 0);//CAS設定狀態 /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev)//遍歷AQS佇列 if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread);//喚醒執行緒 }
h != t