java原始碼-ReentrantLock之NonfairSync
開篇
NonfairSync和FairSync相比而言,多了一次搶佔機會,其他處理邏輯幾乎是一模一樣。
- NonfairSync的tryAcquire的操作流程中如果發現當前鎖未被佔用那麼立即搶佔鎖。
- FairSync的tryAcquire的操作流程中如果發現當前鎖未被還需要繼續判斷當前執行緒否是頭結點才能發起鎖搶佔
java原始碼 – ReentrantLock
java原始碼 – ReentrantLock之FairSync
java原始碼 – ReentrantLock之NonfairSync
java原始碼 – ReentrantLock圖解加鎖過程
加鎖過程
ReentrantLock的的鎖過程如下:
- 1、先嚐試獲取鎖,通過tryAcquire()實現。
- 2、獲取鎖失敗後,執行緒被包裝成Node物件後新增到CLH佇列,通過addWaiter()實現。
- 3、新增CLH佇列後,逐步的去執行CLH佇列的執行緒,如果當前執行緒獲取到了鎖,則返回;否則,當前執行緒進行休眠,直到喚醒並重新獲取鎖了才返回。
public void lock() {
sync.lock();
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire的操作流程
- 1、第一步通過tryAcquire()嘗試獲取鎖,成功則返回
- 2、獲取鎖失敗後通過addWaiter新增到CLH佇列的末尾
- 3、新增CLH佇列後,通過acquireQueued()方法逐步的去執行CLH佇列的執行緒,如果當前執行緒獲取到了鎖則返回;否則當前執行緒進行休眠,直到喚醒並重新獲取鎖後返回。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire的操作流程
- 1、如果鎖未佔用的情況下:當前執行緒直接搶佔鎖並設定鎖佔用執行緒為當前執行緒,非公平鎖NonfairSync和FairSync的差別就在於這個地方,非公平鎖直接搶佔鎖,而公平鎖則需要判斷是否位於頭結點來決定是否搶佔。
- 2、如果鎖被佔用的情況下:判斷當前執行緒是否是佔用鎖執行緒,如果是則實現鎖的可重入功能,設定鎖佔用次數。
- 3、如果上述全否那麼就返回佔鎖失敗的。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
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()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
addWaiter的操作流程
- 1、將當前執行緒包裝成Node物件。
- 2、先嚐試通過快速失敗法嘗試在CLH隊尾插入Node物件
- 3、如果快速插入失敗後那麼就通過enq方法在CLH隊尾插入Node物件
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != 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
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued的操作流程
- 1、如果當前節點Node的前驅節點屬於head,當前節點屬於老二地位通過tryAcquire()嘗試獲取鎖,獲取成功後那麼就釋放原head節點(可以理解為head已經釋放鎖然後從CLH刪除),把當前節點設定為head節點。
- 2、通過shouldParkAfterFailedAcquire()方法判斷Node代表的執行緒是否進入waiting狀態,直到被unpark()。
- 3、parkAndCheckInterrupt()方法將當前執行緒進入waiting狀態。
- 4、休眠執行緒被喚醒的時候會執行 if (p == head && tryAcquire(arg))邏輯判斷
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);
}
}
shouldParkAfterFailedAcquire的操作流程
- 1、如果前置節點處於SIGNAL狀態,那麼當前執行緒進入阻塞狀態,返回true
- 2、如果前置節點處於ws>0也就是取消狀態,那麼當前執行緒節點就往前查詢第一個狀態處於ws<=0的節點
- 3、如果前置狀態ws=0的節點,那麼就把前置節點設定為SIGNAL狀態
- 4、整個shouldParkAfterFailedAcquire函式是在for()迴圈當中迴圈執行的,我們可以想象按照步驟2->3->1的順序執行,按照前置遍歷尋找合適的前置節點,接著發現前置節點ws狀態為0後重新設定為SIGNAL,最後發現前置節點狀態為SINGAL後休眠執行緒自身。
- 5、執行緒從執行態進入waiting狀態其實也是經歷了一系列的處理過程的。
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;
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
解鎖過程
release過程
- 1、通過tryRelease()方法嘗試讓當前執行緒釋放鎖物件
- 2、通過unparkSuccessor()方法設定當前節點狀態ws=0並且喚醒CLH佇列中的下一個等待執行緒
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease過程
- 1、如果佔用鎖執行緒非當前執行緒直接拋異常
- 2、遞減鎖計數後如果值為0那麼就釋放當前鎖佔用者
- 3、更新鎖狀態為未佔用,即state為0
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;
}
unparkSuccessor過程
- 1、設定當前Node狀態為0
- 2、尋找下一個等待執行緒節點來喚醒等待執行緒並通過LockSupport.unpark()喚醒執行緒
- 3、尋找下一個等待執行緒,如果當前Node的下一個節點符合狀態就直接進行喚醒,否則從隊尾開始進行倒序查詢,找到最優先的執行緒進行喚醒。
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原始碼 - ReentrantLock之FairSyncJava原始碼ReentrantLockAI
- java原始碼-ReentrantLock之FairSyncJava原始碼ReentrantLockAI
- Java讀原始碼之ReentrantLock(2)Java原始碼ReentrantLock
- Java併發之ReentrantLock原始碼解析(一)JavaReentrantLock原始碼
- Java併發之ReentrantLock原始碼解析(二)JavaReentrantLock原始碼
- Java併發之ReentrantLock原始碼解析(三)JavaReentrantLock原始碼
- Java併發之ReentrantLock原始碼解析(四)JavaReentrantLock原始碼
- JUC之ReentrantLock原始碼分析ReentrantLock原始碼
- Java 併發程式設計(十二) -- ReentrantLock中的非公平鎖NonfairSyncJava程式設計ReentrantLockAI
- ReentrantLock原始碼ReentrantLock原始碼
- ReentrantLock原始碼解析ReentrantLock原始碼
- ReentrantLock原始碼分析ReentrantLock原始碼
- 【JUC】JDK1.8原始碼分析之ReentrantLock(三)JDK原始碼ReentrantLock
- ReentrantLock原始碼解讀ReentrantLock原始碼
- Java 併發程式設計(九) -- ReentrantLock 原始碼分析Java程式設計ReentrantLock原始碼
- Java鎖之ReentrantLock(一)JavaReentrantLock
- Java鎖之ReentrantLock(二)JavaReentrantLock
- ReentrantLock解析及原始碼分析ReentrantLock原始碼
- 【JDK】JDK原始碼分析-ReentrantLockJDK原始碼ReentrantLock
- 深入淺出ReentrantLock原始碼解析ReentrantLock原始碼
- ReentrantLock介紹及原始碼解析ReentrantLock原始碼
- Java ReEntrantLock 之 Condition條件(Java程式碼實戰-002)JavaReentrantLock
- ReentrantLock可重入鎖——原始碼詳解ReentrantLock原始碼
- synchronized實現原理及ReentrantLock原始碼synchronizedReentrantLock原始碼
- ReentrantLock 公平鎖原始碼 第0篇ReentrantLock原始碼
- ReentrantLock 公平鎖原始碼 第1篇ReentrantLock原始碼
- ReentrantLock 公平鎖原始碼 第2篇ReentrantLock原始碼
- 不得不知的ReentrantLock原始碼ReentrantLock原始碼
- Java ReEntrantLock (Java程式碼實戰-001)JavaReentrantLock
- AQS原始碼深入分析之獨佔模式-ReentrantLock鎖特性詳解AQS原始碼模式ReentrantLock
- 從ReentrantLock詳解AQS原理原始碼解析ReentrantLockAQS原始碼
- Java集合框架之 Java HashMap 原始碼解析Java框架HashMap原始碼
- Java集合之Hashtable原始碼解析Java原始碼
- java併發之hashmap原始碼JavaHashMap原始碼
- Java集合之ArrayList原始碼解析Java原始碼
- java原始碼學習之EnumJava原始碼
- Java集合之HashMap原始碼解析JavaHashMap原始碼
- Java併發包原始碼學習系列:ReentrantLock可重入獨佔鎖詳解Java原始碼ReentrantLock