java ReentrantLock條件鎖實現(xiàn)原理示例詳解
引言
在前兩篇文章中,我們了解了ReentrantLock內部公平鎖和非公平鎖的實現(xiàn)原理,可以知道其底層基于AQS,使用雙向鏈表實現(xiàn),同時在線程間通信方式(2)中我們了解到ReentrantLock也是支持條件鎖的,接下來我們來看下,其內部條件鎖的實現(xiàn)原理。
條件鎖的使用
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
@Override
public void run() {
lock.lock();
System.out.println(Thread.currentThread().getName()+" enter lock first");
System.out.println(Thread.currentThread().getName()+" await start");
try {
condition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+" await end");
lock.unlock();
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
lock.lock();
System.out.println(Thread.currentThread().getName()+" enter lock first");
System.out.println(Thread.currentThread().getName()+" start sleep");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+" end sleep");
System.out.println(Thread.currentThread().getName()+" signalAll condition");
condition.signalAll();
System.out.println(Thread.currentThread().getName()+"signal end");
lock.unlock();
}
});
}
如上代碼所示,一般情況下我們通過
Condition condition = lock.newCondition();
創(chuàng)建條件對象,使用condition.await();表示當前線程需要等待條件才能繼續(xù)執(zhí)行,當線程執(zhí)行到此處時,會進入等待隊列等待,直到有另一個線程通過condition.signalAll();或condition.signal();喚醒,此時表明當前線程執(zhí)行條件已具備,此時當前線程繼續(xù)執(zhí)行,上述代碼中,當前線程會轉入AQS的同步等待隊列中,去等待搶占lock鎖,其運行結果如下圖所示:

條件鎖一般適用于線程需要具備一定條件后才能正確執(zhí)行的情況。
ReentrantLock.newCondition()
上文看到Condition的創(chuàng)建和基本用法,接下來我們來看下Condition的實現(xiàn)原理,跟蹤ReentrantLock的執(zhí)行代碼如下所示:
?// ReentrantLock.java
?public Condition newCondition() {
? ? ?return sync.newCondition();
?}
??
?// ReentrantLock內部類Sync中
?final ConditionObject newCondition() {
? ? ?return new ConditionObject();
?}可以看到newCondition最終返回了一個ConditionObject類的對象,ConditionObject類代碼如下所示:
// AQS中聲明的ConditionObject
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
private transient Node firstWaiter;
private transient Node lastWaiter;
public ConditionObject() { }
private Node addConditionWaiter() {
}
private void doSignal(Node first) {
.....
}
private void doSignalAll(Node first) {
.....
}
private void unlinkCancelledWaiters() {
.....
}
相信大家已經(jīng)看出來了,很熟悉的Node鏈表有沒有?其中firstWaiter指向鏈表首位,lastWaiter指向鏈表尾,在該鏈表內維護一個Node的雙向鏈表,結合AQS中實現(xiàn),我們可以猜測出,在condition.await的時候會以當前線程創(chuàng)建Node節(jié)點,隨后以插入條件隊列,隨后當執(zhí)行condition.signal/condition.signalAll時,喚醒在鏈表上的這些節(jié)點,具體實現(xiàn)是不是這樣呢?我們繼續(xù)看
Condition.await
ConditionObject實現(xiàn)的await方法如下所示:
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 以當前線程創(chuàng)建Node對象,并添加值隊尾
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
// 通過LockSupport阻塞線程
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
Condition.signal
ConditionObject中的signal函數(shù)實現(xiàn)如下所示:
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
// 對隊首節(jié)點喚醒
doSignal(first);
}
private void doSignal(Node first) {
do {
// 重置firstWaiter并不斷嘗試喚醒首節(jié)點
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
// 嘗試更新節(jié)點的waitStatus
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 當前線程可以正常執(zhí)行了,將該節(jié)點移入同步等待隊列中,嘗試獲取鎖
Node p = enq(node);
int ws = p.waitStatus;
// 如果可以獲取鎖,則立即喚醒執(zhí)行
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
Condition.signalAll的邏輯與signal基本一致,區(qū)別在于是將在該條件上等待的所有節(jié)點均移入同步等待隊列中。
以上就是java ReentrantLock條件鎖實現(xiàn)原理示例詳解的詳細內容,更多關于java ReentrantLock條件鎖的資料請關注腳本之家其它相關文章!
相關文章
java?-jar命令及SpringBoot通過java?-jav啟動項目的過程
本篇文章將為大家講述關于 SpringBoot 項目工程完成后,是如何通過 java-jar 命令來啟動的,以及介紹 java-jar 命令的詳細內容,對SpringBoot java?-jav啟動過程感興趣的朋友跟隨小編一起看看吧2023-05-05
springboot日期格式化全局LocalDateTime詳解
文章主要分析了Spring Boot中ObjectMapper對象的序列化和反序列化過程,并具體探討了日期格式化問題,通過分析Spring Boot的自動配置類JacksonAutoConfiguration,文章詳細說明了ObjectMapper對象的創(chuàng)建和配置過程2025-02-02
Spring?Data?JPA框架快速入門之自定義Repository接口
Spring?Data?JPA是Spring基于JPA規(guī)范的基礎上封裝的?套?JPA?應?框架,可使開發(fā)者?極簡的代碼即可實現(xiàn)對數(shù)據(jù)庫的訪問和操作,本篇我們來了解Spring?Data?JPA框架的自定義Repository接口2022-04-04

