亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術(shù)文章
文章詳情頁

Java多線程Condition接口原理介紹

瀏覽:4日期:2022-08-23 13:51:38

Condition接口提供了類似Object的監(jiān)視器方法,與Lock配合可以實(shí)現(xiàn)等待/通知模式,但是這兩者在使用方式以及功能特性上還是有差別的

Java多線程Condition接口原理介紹

Condition接口詳解

Condition定義了等待/通知兩種類型的方法,當(dāng)前線程調(diào)用這些方法時,需要提前獲取到Condition對象關(guān)聯(lián)的鎖。Condition對象是由Lock對象(調(diào)用Lock對象的newCondition()方法)創(chuàng)建出來的,換句話說,Condition是依賴Lock對象的。

Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void conditionWait() throws InterruptedException { lock.lock(); try { condition.await(); } finally { lock.unlock(); } } public void conditionSignal() throws InterruptedException { lock.lock(); try { condition.signal(); } finally { lock.unlock(); } }

一般都會將Condition對象作為成員變量。當(dāng)調(diào)用await()方法后,當(dāng)前線程會釋放鎖并在此等待,而其他線程調(diào)用Condition對象的signal()方法,通知當(dāng)前線程后,當(dāng)前線程才從await()方法返回,并且在返回前已經(jīng)獲取了鎖。

/** * 當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知(signal)或中斷,當(dāng)前線程進(jìn)入后臺運(yùn)行狀態(tài)且從await()方法返回 * 其他線程調(diào)用該Condition的signal或者signalAll方法,而當(dāng)前線程被選中喚醒 * 1、其他線程(interrupt)中斷當(dāng)前線程 * 2、如果當(dāng)前等待線程從await方法返回,那么表明當(dāng)前線程已經(jīng)獲取了Condition對象的鎖 */ void await() throws InterruptedException; /** * 當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知,對中斷不響應(yīng) */ void awaitUninterruptibly(); /** * <pre> {@code * boolean aMethod(long timeout, TimeUnit unit) { * long nanos = unit.toNanos(timeout); * lock.lock(); * try { * while (!conditionBeingWaitedFor()) { * if (nanos <= 0L) * return false; * nanos = theCondition.awaitNanos(nanos); * } * // ... * } finally { * lock.unlock(); * } * }}</pre> * 當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知、中斷或超時。返回值表示剩余時間,如果在nanosTimeout納秒之前被喚醒,那么返回值就是nanosTimeout-實(shí)際耗時 * 返回值<=0說明超時 * */ long awaitNanos(long nanosTimeout) throws InterruptedException; /** * 當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知、中斷或超時,如果沒有到指定時間被通知返回true,否則返回false */ boolean await(long time, TimeUnit unit) throws InterruptedException; /** * 喚醒一個等待在Condition上的線程,該線程從等待方法返回之前必須獲得與Condition相關(guān)聯(lián)的鎖 */ void signal();

獲取一個Condition必須通過Lock的newCondition()方法。下面通過一個有界隊列的示例來深入了解Condition的使用方式。

有界隊列是一種特殊的隊列,當(dāng)隊列為空時,隊列的獲取操作將會阻塞獲取線程,直到隊列中有新增元素,當(dāng)隊列已滿時,隊列的插入操作將會阻塞插入線程,直到隊列出現(xiàn)“空位”

public class BoundedQueue<T> { private Object[] items; // 添加的下標(biāo),刪除的下標(biāo)和數(shù)組當(dāng)前數(shù)量 private int addIndex,removeIndex,count; private Lock lock = new ReentrantLock(); private Condition notEmpty = lock.newCondition(); private Condition notFull = lock.newCondition(); public BoundedQueue(int size){ items = new Object[size]; } /** * 添加一個元素,如果數(shù)組滿,則添加線程進(jìn)入等待狀態(tài),直到有'空位' * @author fuyuwei * 2017年5月21日 下午6:14:55 * @param t * @throws InterruptedException */ public void add(T t) throws InterruptedException{ lock.lock(); try{ while(count == items.length){ notFull.await(); } items[addIndex] = t; if(++addIndex == items.length) addIndex = 0; ++count; notEmpty.signal(); }finally{ lock.unlock(); } } /** * 由頭部刪除一個元素,如果數(shù)組空,則刪除線程進(jìn)入等待狀態(tài),直到有新添加元素 * @author fuyuwei * 2017年5月21日 下午6:20:54 * @return * @throws InterruptedException */ @SuppressWarnings('unchecked') public T remove() throws InterruptedException{ lock.lock(); try{ while(count == 0) notEmpty.await(); Object x = items[removeIndex]; if(++removeIndex == items.length) removeIndex = 0; --count; notFull.signal(); return (T)x; }finally{ lock.unlock(); } }}

首先需要獲得鎖,目的是確保數(shù)組修改的可見性和排他性。當(dāng)數(shù)組數(shù)量等于數(shù)組長度時,表示數(shù)組已滿,則調(diào)用notFull.await(),當(dāng)前線程隨之釋放鎖并進(jìn)入等待狀態(tài)。如果數(shù)組數(shù)量不等于數(shù)組長度,表示數(shù)組未滿,則添加元素到數(shù)組中,同時通知等待在notEmpty上的線程,數(shù)組中已經(jīng)有新元素可以獲取。

在添加和刪除方法中使用while循環(huán)而非if判斷,目的是防止過早或意外的通知,只有條件符合才能夠退出循環(huán)。回想之前提到的等待/通知的經(jīng)典范式,二者是非常類似的

Condition原理分析

ConditionObject是同步器AbstractQueuedSynchronizer的內(nèi)部類,因?yàn)镃ondition的操作需要獲取相關(guān)聯(lián)的鎖,所以作為同步器的內(nèi)部類也較為合理。每個Condition對象都包含著一個隊列,該隊列是Condition對象實(shí)現(xiàn)等待/通知功能的關(guān)鍵。下面將分析Condition的實(shí)現(xiàn),主要包括:等待隊列、等待和通知

等待隊列

等待隊列是一個FIFO的隊列,在隊列中的每個節(jié)點(diǎn)都包含了一個線程引用,該線程就是在Condition對象上等待的線程,如果一個線程調(diào)用了Condition.await()方法,那么該線程將會釋放鎖、構(gòu)造成節(jié)點(diǎn)加入等待隊列并進(jìn)入等待狀態(tài)

一個Condition包含一個等待隊列,Condition擁有首節(jié)點(diǎn)(firstWaiter)和尾節(jié)點(diǎn)(lastWaiter)。當(dāng)前線程調(diào)用Condition.await()方法,將會以當(dāng)前線程構(gòu)造節(jié)點(diǎn),并將節(jié)點(diǎn)從尾部加入等待隊列,等待隊列的基本結(jié)構(gòu)如下圖所示

Java多線程Condition接口原理介紹

如圖所示,Condition擁有首尾節(jié)點(diǎn)的引用,而新增節(jié)點(diǎn)只需要將原有的尾節(jié)點(diǎn)nextWaiter指向它,并且更新尾節(jié)點(diǎn)即可。上述節(jié)點(diǎn)引用更新的過程并沒有使用CAS保證,原因在于調(diào)用await()方法的線程必定是獲取了鎖的線程,也就是說該過程是由鎖來保證線程安全的。在Object的監(jiān)視器模型上,一個對象擁有一個同步隊列和等待隊列,而并發(fā)包中的Lock(更確切地說是同步器)擁有一個同步隊列和多個等待隊列,其對應(yīng)關(guān)系如下圖所示

Java多線程Condition接口原理介紹

等待

調(diào)用Condition的await()方法(或者以await開頭的方法),會使當(dāng)前線程進(jìn)入等待隊列并釋放鎖,同時線程狀態(tài)變?yōu)榈却隣顟B(tài)。當(dāng)從await()方法返回時,當(dāng)前線程一定獲取了Condition相關(guān)聯(lián)的鎖。

如果從隊列(同步隊列和等待隊列)的角度看await()方法,當(dāng)調(diào)用await()方法時,相當(dāng)于同步隊列的首節(jié)點(diǎn)(獲取了鎖的節(jié)點(diǎn))移動到Condition的等待隊列中

public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); // 當(dāng)前線程加入等待隊列 Node node = addConditionWaiter(); // 釋放同步狀態(tài),也就是釋放鎖 int savedState = fullyRelease(node); int interruptMode = 0; 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) unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); }

調(diào)用該方法的線程成功獲取了鎖的線程,也就是同步隊列中的首節(jié)點(diǎn),該方法會將當(dāng)前線程構(gòu)造成節(jié)點(diǎn)并加入等待隊列中,然后釋放同步狀態(tài),喚醒同步隊列中的后繼節(jié)點(diǎn),然后當(dāng)前線程會進(jìn)入等待狀態(tài)。當(dāng)?shù)却犃兄械墓?jié)點(diǎn)被喚醒,則喚醒節(jié)點(diǎn)的線程開始嘗試獲取同步狀態(tài)。如果不是通過其他線程調(diào)用Condition.signal()方法喚醒,而是對等待線程進(jìn)行中斷,則會拋出InterruptedException

通知

調(diào)用Condition的signal()方法,將會喚醒在等待隊列中等待時間最長的節(jié)點(diǎn)(首節(jié)點(diǎn)),在喚醒節(jié)點(diǎn)之前,會將節(jié)點(diǎn)移到同步隊列中

Java多線程Condition接口原理介紹

public final void signal() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignal(first); }

調(diào)用該方法的前置條件是當(dāng)前線程必須獲取了鎖,可以看到signal()方法進(jìn)行了isHeldExclusively()檢查,也就是當(dāng)前線程必須是獲取了鎖的線程。接著獲取等待隊列的首節(jié)點(diǎn),將其移動到同步隊列并使用LockSupport喚醒節(jié)點(diǎn)中的線程

節(jié)點(diǎn)從等待隊列移動到同步隊列的過程如下圖所示

Java多線程Condition接口原理介紹

通過調(diào)用同步器的enq(Node node)方法,等待隊列中的頭節(jié)點(diǎn)線程安全地移動到同步隊列。當(dāng)節(jié)點(diǎn)移動到同步隊列后,當(dāng)前線程再使用LockSupport喚醒該節(jié)點(diǎn)的線程。

被喚醒后的線程,將從await()方法中的while循環(huán)中退出(isOnSyncQueue(Node node)方法返回true,節(jié)點(diǎn)已經(jīng)在同步隊列中),進(jìn)而調(diào)用同步器的acquireQueued()方法加入到獲取同步狀態(tài)的競爭中。

成功獲取同步狀態(tài)(或者說鎖)之后,被喚醒的線程將從先前調(diào)用的await()方法返回,此時該線程已經(jīng)成功地獲取了鎖。

Condition的signalAll()方法,相當(dāng)于對等待隊列中的每個節(jié)點(diǎn)均執(zhí)行一次signal()方法,效果就是將等待隊列中所有節(jié)點(diǎn)全部移動到同步隊列中,并喚醒每個節(jié)點(diǎn)的線程。

以上這篇Java多線程Condition接口原理介紹就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 色婷在线| 亚洲成在人网站天堂一区二区 | 一级做a毛片免费视频 | 一级做a爱过程免费视频麻豆 | 性生活大片免费看 | 22eee在线播放成人免费视频 | 色播欧美 | 91嫩草国产在线观看免费 | 国产91青青成人a在线 | 看免费的毛片 | 非洲特黄一级毛片高清视频 | 免费污片在线观看 | 成人午夜亚洲影视在线观看 | 午夜视频吧| 免费在线色视频 | 亚洲最大免费视频网 | 中文字幕三级久久久久久 | 国产精品久久久免费视频 | 国产精品入口在线看麻豆 | 美女va | 老司机深夜福利影院 | 香蕉香蕉国产片一级一级毛片 | 国产精品99在线观看 | 91国在线观看 | 欧美黄a | 麻豆果冻精品一区二区 | 久久午夜鲁丝片午夜精品 | 国产2021成人精品 | 成人一级片在线观看 | 国产精品欧美日韩视频一区 | 高中生精品视频在线观看 | 日韩一区二区久久久久久 | 亚洲视频免费在线 | 亚洲香蕉久久综合网 | 亚拍一区 | 欧美一级高清片欧美国产欧美 | 麻豆回家视频区一区二 | 欧美高清另类 | 2022色婷婷综合久久久 | 青青草一区国产97 | 国产精品黄大片观看 |