一 UML類圖
1.1、ReentrantLock
通過類圖ReentrantLock是同步鎖,同一時間只能有一個線程獲取到鎖,其他獲取該鎖的線程會被阻塞而被放入AQS阻塞隊列中。ReentrantLock類繼承Lock接口;內部抽象類Sync實現抽象隊列同步器AbstractQueuedSynchronizer;Sync類有兩個子類NonfairSync(非公平鎖)和FairSync(公平鎖),默認構造方法使用非公平鎖,可以使用帶布爾參數的構造方法指定使用公平鎖;ReentrantLock可以創建多個條件進行綁定。
1.2、AbstractQueuedSynchronizer
AbstractQueuedSynchronizer:抽象隊列同步器,維護一個volatile int state變量標識共享資源和一個FIFO線程阻塞隊列(AQS隊列)。
protected final void setState(int newState):設置state值
protected final int getState():獲取state值
protected final boolean compareAndSetState(int expect, int update):CAS設置state值
AQS有兩種共享資源類型:SHARED(共享)和EXCLUSIVE(獨佔),針對類型有不同的方法:
protected boolean tryAcquire(int arg):獨佔類型獲取鎖
protected boolean tryRelease(int arg):獨佔類型釋放鎖
protected int tryAcquireShared(int arg):共享類型獲取鎖
protected boolean tryReleaseShared(int arg):共享類型釋放鎖
protected boolean isHeldExclusively():是否是獨佔類型
1.3、線程節點類型waitStatus
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; //釋放鎖是需要通知其他節點
二 原理分析
2.1 獲取鎖
2.1.1 void lock()方法
調用線程T調用該方法嘗試獲取當前鎖。
①如果鎖未被其他線程獲取,則調用線程T成功獲取到當前鎖,然後設置當前鎖的擁有者為調用線程T,並設置AQS的狀態值state為1,然後直接返回。
②如果調用線程T之前已經獲取當前鎖,則只設置設置AQS的狀態值state加1,然後返回。
③如果當前鎖已被其他線程獲取,則調用線程T放入AQS隊列后阻塞掛起。
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節點是頭結點,node作為隊列第二個節點 setHead(node);//將頭節點設置為node節點,node節點出隊列 p.next = null; //原頭結點設置為空,便於GC回收 failed = false; return interrupted; } 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; }
//阻塞掛起當前線程
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);//大於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
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※教你寫出一流的銷售文案?
※別再煩惱如何寫文案,掌握八大原則!