java ReentrantLock條件鎖實(shí)現(xiàn)原理示例詳解
引言
在前兩篇文章中,我們了解了ReentrantLock內(nèi)部公平鎖和非公平鎖的實(shí)現(xiàn)原理,可以知道其底層基于AQS,使用雙向鏈表實(shí)現(xiàn),同時在線程間通信方式(2)中我們了解到ReentrantLock也是支持條件鎖的,接下來我們來看下,其內(nèi)部條件鎖的實(shí)現(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();
表示當(dāng)前線程需要等待條件才能繼續(xù)執(zhí)行,當(dāng)線程執(zhí)行到此處時,會進(jìn)入等待隊列等待,直到有另一個線程通過condition.signalAll();
或condition.signal();
喚醒,此時表明當(dāng)前線程執(zhí)行條件已具備,此時當(dāng)前線程繼續(xù)執(zhí)行,上述代碼中,當(dāng)前線程會轉(zhuǎn)入AQS的同步等待隊列中,去等待搶占lock鎖,其運(yùn)行結(jié)果如下圖所示:
條件鎖一般適用于線程需要具備一定條件后才能正確執(zhí)行的情況。
ReentrantLock.newCondition()
上文看到Condition的創(chuàng)建和基本用法,接下來我們來看下Condition的實(shí)現(xiàn)原理,跟蹤ReentrantLock的執(zhí)行代碼如下所示:
?// ReentrantLock.java ?public Condition newCondition() { ? ? ?return sync.newCondition(); ?} ?? ?// ReentrantLock內(nèi)部類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指向鏈表尾,在該鏈表內(nèi)維護(hù)一個Node的雙向鏈表,結(jié)合AQS中實(shí)現(xiàn),我們可以猜測出,在condition.await的時候會以當(dāng)前線程創(chuàng)建Node節(jié)點(diǎn),隨后以插入條件隊列,隨后當(dāng)執(zhí)行condition.signal/condition.signalAll時,喚醒在鏈表上的這些節(jié)點(diǎn),具體實(shí)現(xiàn)是不是這樣呢?我們繼續(xù)看
Condition.await
ConditionObject實(shí)現(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(); // 以當(dāng)前線程創(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ù)實(shí)現(xiàn)如下所示:
public final void signal() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) // 對隊首節(jié)點(diǎn)喚醒 doSignal(first); }
private void doSignal(Node first) { do { // 重置firstWaiter并不斷嘗試喚醒首節(jié)點(diǎn) if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null); }
final boolean transferForSignal(Node node) { // 嘗試更新節(jié)點(diǎn)的waitStatus if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; // 當(dāng)前線程可以正常執(zhí)行了,將該節(jié)點(diǎn)移入同步等待隊列中,嘗試獲取鎖 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é)點(diǎn)均移入同步等待隊列中。
以上就是java ReentrantLock條件鎖實(shí)現(xiàn)原理示例詳解的詳細(xì)內(nèi)容,更多關(guān)于java ReentrantLock條件鎖的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java基于JNDI 實(shí)現(xiàn)讀寫分離的示例代碼
本文主要介紹了Java基于JNDI 實(shí)現(xiàn)讀寫分離的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12Mybatis之Mapper動態(tài)代理實(shí)例解析
這篇文章主要介紹了Mybatis之Mapper動態(tài)代理實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-08-08Java語言實(shí)現(xiàn)簡單FTP軟件 FTP本地文件管理模塊實(shí)現(xiàn)(9)
這篇文章主要為大家詳細(xì)介紹了Java語言實(shí)現(xiàn)簡單FTP軟件,F(xiàn)TP本地文件管理模塊的實(shí)現(xiàn)方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04Java IO學(xué)習(xí)之緩沖輸入流(BufferedInputStream)
這篇文章主要介紹了Java IO學(xué)習(xí)之緩沖輸入流(BufferedInputStream)的相關(guān)資料,需要的朋友可以參考下2017-02-02jar的MANIFEST.MF配置Class-Path, java -classpath設(shè)置無效的解
這篇文章主要介紹了jar的MANIFEST.MF配置Class-Path, java -classpath設(shè)置無效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07