java并發(fā)中DelayQueue延遲隊(duì)列原理剖析
介紹
DelayQueue隊(duì)列是一個(gè)延遲隊(duì)列,DelayQueue中存放的元素必須實(shí)現(xiàn)Delayed接口的元素,實(shí)現(xiàn)接口后相當(dāng)于是每個(gè)元素都有個(gè)過(guò)期時(shí)間,當(dāng)隊(duì)列進(jìn)行take獲取元素時(shí),先要判斷元素有沒(méi)有過(guò)期,只有過(guò)期的元素才能出隊(duì)操作,沒(méi)有過(guò)期的隊(duì)列需要等待剩余過(guò)期時(shí)間才能進(jìn)行出隊(duì)操作。
源碼分析
DelayQueue隊(duì)列內(nèi)部使用了PriorityQueue優(yōu)先隊(duì)列來(lái)進(jìn)行存放數(shù)據(jù),它采用的是二叉堆進(jìn)行的優(yōu)先隊(duì)列,使用ReentrantLock鎖來(lái)控制線程同步,由于內(nèi)部元素是采用的PriorityQueue來(lái)進(jìn)行存放數(shù)據(jù),所以Delayed接口實(shí)現(xiàn)了Comparable接口,用于比較來(lái)控制優(yōu)先級(jí),如下代碼所示:
public interface Delayed extends Comparable<Delayed> {
/**
* Returns the remaining delay associated with this object, in the
* given time unit.
*
* @param unit the time unit
* @return the remaining delay; zero or negative values indicate
* that the delay has already elapsed
*/
long getDelay(TimeUnit unit);
}
DelayQueue的成員變量如下所示:
// 鎖。 private final transient ReentrantLock lock = new ReentrantLock(); // 優(yōu)先隊(duì)列。 private final PriorityQueue<E> q = new PriorityQueue<E>(); /** * Leader-Follower的變種。 * Thread designated to wait for the element at the head of * the queue. This variant of the Leader-Follower pattern * (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to * minimize unnecessary timed waiting. When a thread becomes * the leader, it waits only for the next delay to elapse, but * other threads await indefinitely. The leader thread must * signal some other thread before returning from take() or * poll(...), unless some other thread becomes leader in the * interim. Whenever the head of the queue is replaced with * an element with an earlier expiration time, the leader * field is invalidated by being reset to null, and some * waiting thread, but not necessarily the current leader, is * signalled. So waiting threads must be prepared to acquire * and lose leadership while waiting. */ private Thread leader = null; /** * Condition signalled when a newer element becomes available * at the head of the queue or a new thread may need to * become leader. */ // 條件,代表如果有數(shù)據(jù)則通知Follower線程,喚醒線程處理隊(duì)列內(nèi)容。 private final Condition available = lock.newCondition();
Leader-Follower模式的變種,用于最小化不必要的定時(shí)等待,當(dāng)一個(gè)線程被選擇為L(zhǎng)eader時(shí),它會(huì)等待延遲過(guò)去執(zhí)行代碼邏輯,而其他線程則需要無(wú)限期等待,在從take或poll返回之前,每當(dāng)隊(duì)列的頭部被替換為具有更早到期時(shí)間的元素時(shí),leader字段將通過(guò)重置為空而無(wú)效,Leader線程必須向其中一個(gè)Follower線程發(fā)出信號(hào),被喚醒的 follwer 線程被設(shè)置為新的Leader 線程。
offer操作
public boolean offer(E e) {
// 獲取到鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 將元素存儲(chǔ)到PriorityQueue優(yōu)先隊(duì)列中
q.offer(e);
// 如果第一個(gè)元素是當(dāng)前元素,說(shuō)明之前隊(duì)列中為空,則先將Leader設(shè)置為空,通知等待線程可以爭(zhēng)搶Leader了。
if (q.peek() == e) {
leader = null;
available.signal();
}
// 返回成功
return true;
} finally {
lock.unlock();
}
}
offer操作前先進(jìn)行獲取鎖的操作,也就是同一時(shí)間內(nèi)只能有一個(gè)線程可以入隊(duì)操作。
- 獲取到ReentrantLock鎖對(duì)象。
- 將元素添加到PriorityQueue優(yōu)先隊(duì)列中
- 如果隊(duì)列中最早過(guò)期的元素是自己,則說(shuō)明隊(duì)列原先是空的,所以將Leader進(jìn)行重置,通知Follower線程可以成為L(zhǎng)eader線程。
- 最后進(jìn)行解鎖操作。
put操作
put操作其實(shí)就是調(diào)用的offer操作來(lái)進(jìn)行添加數(shù)據(jù)的,以下是源碼信息:
public void put(E e) {
offer(e);
}
take操作
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
// 獲取可中斷的鎖。
lock.lockInterruptibly();
try {
// 循環(huán)獲取數(shù)據(jù)。
for (;;) {
// 獲取最早過(guò)期的元素,但是不彈出對(duì)象。
E first = q.peek();
// 如果最早過(guò)期的元素為空,說(shuō)明隊(duì)列為空,則線程直接進(jìn)入無(wú)限期等待,并且讓出鎖。
if (first == null)
// 當(dāng)前線程無(wú)限期等待,直到被喚醒,并且讓出鎖對(duì)象。
available.await();
else {
// 獲取最早過(guò)期的元素剩余過(guò)期時(shí)間。
long delay = first.getDelay(NANOSECONDS);
// 如果剩余過(guò)期時(shí)間小于0,則說(shuō)明已經(jīng)過(guò)期,反之還沒(méi)有過(guò)期。
if (delay <= )
// 如果已經(jīng)過(guò)期直接獲取最早過(guò)期的元素,并返回。
return q.poll();
// 如果剩余過(guò)期日期大于0,則會(huì)進(jìn)入到這里。
// 將剛才獲取的最早過(guò)期的元素設(shè)置為空。
first = null; // don't retain ref while waiting
// 如果有線程爭(zhēng)搶的Leader線程,則進(jìn)行無(wú)限期等待。
if (leader != null)
// 無(wú)限期等待并讓出鎖。
available.await();
else {
// 獲取當(dāng)前線程。
Thread thisThread = Thread.currentThread();
// 設(shè)置當(dāng)前線程變?yōu)長(zhǎng)eader線程。
leader = thisThread;
try {
// 等待剩余等待時(shí)間。
available.awaitNanos(delay);
} finally {
// 將Leader設(shè)置為null。
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
// 如果隊(duì)列不為空,并且沒(méi)有Leader則通知等待線程可以成為L(zhǎng)eader。
if (leader == null && q.peek() != null)
// 通知等待線程。
available.signal();
lock.unlock();
}
}
- 當(dāng)獲取元素時(shí),先獲取到鎖對(duì)象。
- 獲取最早過(guò)期的元素,但是并不從隊(duì)列中彈出元素。
- 最早過(guò)期元素是否為空,如果為空則直接讓當(dāng)前線程無(wú)限期等待狀態(tài),并且讓出當(dāng)前鎖對(duì)象。
- 如果最早過(guò)期的元素不為空
- 獲取最早過(guò)期元素的剩余過(guò)期時(shí)間,如果已經(jīng)過(guò)期則直接返回當(dāng)前元素
- 如果沒(méi)有過(guò)期,也就是說(shuō)剩余時(shí)間還存在,則先獲取Leader對(duì)象,如果Leader已經(jīng)有線程在處理,則當(dāng)前線程進(jìn)行無(wú)限期等待,如果Leader為空,則首先將Leader設(shè)置為當(dāng)前線程,并且讓當(dāng)前線程等待剩余時(shí)間。
- 最后將Leader線程設(shè)置為空
- 如果Leader已經(jīng)為空,并且隊(duì)列有內(nèi)容則喚醒一個(gè)等待的隊(duì)列。
poll操作
獲取最早過(guò)期的元素,如果隊(duì)列頭沒(méi)有過(guò)期的元素則直接返回null,反之返回過(guò)期的元素。
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
// 如果隊(duì)列為空或者隊(duì)列最早過(guò)期的元素沒(méi)有過(guò)期,則返回null。
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
// 出隊(duì)列操作。
return q.poll();
} finally {
lock.unlock();
}
}
小結(jié)
- DelayQueue是一個(gè)無(wú)界的并發(fā)延遲阻塞隊(duì)列,隊(duì)列中的元素必須實(shí)現(xiàn)Delayed接口,相應(yīng)了需要實(shí)現(xiàn)Comparable接口實(shí)現(xiàn)比較的方法
- Leader-Follower模式的變種,用于最小化不必要的定時(shí)等待,當(dāng)一個(gè)線程被選擇為L(zhǎng)eader時(shí),它會(huì)等待延遲過(guò)去執(zhí)行代碼邏輯,而其他線程則需要無(wú)限期等待,在從take或poll返回之前,每當(dāng)隊(duì)列的頭部被替換為具有更早到期時(shí)間的元素時(shí),leader字段將通過(guò)重置為空而無(wú)效,Leader線程必須向其中一個(gè)Follower線程發(fā)出信號(hào),被喚醒的 follwer 線程被設(shè)置為新的Leader 線程。
到此這篇關(guān)于java并發(fā)中DelayQueue延遲隊(duì)列原理剖析的文章就介紹到這了,更多相關(guān)java DelayQueue延遲隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解Java線程池隊(duì)列中的延遲隊(duì)列DelayQueue
- Java延遲隊(duì)列DelayQueue原理詳解
- Java中的延遲隊(duì)列DelayQueue詳細(xì)解析
- Java阻塞延遲隊(duì)列DelayQueue原理及使用詳解
- Java中的延遲隊(duì)列DelayQueue源碼解析
- JAVA中的延遲隊(duì)列DelayQueue應(yīng)用解析
- Java的延遲隊(duì)列之DelayQueue解讀
- Java的DelayQueue延遲隊(duì)列簡(jiǎn)單使用代碼實(shí)例
- Java實(shí)現(xiàn)DelayQueue延遲隊(duì)列示例代碼
相關(guān)文章
springboot-2.3.x最新版源碼閱讀環(huán)境搭建(基于gradle構(gòu)建)
這篇文章主要介紹了springboot-2.3.x最新版源碼閱讀環(huán)境搭建(基于gradle構(gòu)建),需要的朋友可以參考下2020-08-08
Mybatis Plus Wrapper查詢(xún)某幾列的方法實(shí)現(xiàn)
MybatisPlus中,使用Wrapper的select和notSelect方法可以精確控制查詢(xún)的字段,本文就來(lái)介紹一下Mybatis Plus Wrapper查詢(xún)某幾列的方法實(shí)現(xiàn),感興趣的可以了解一下2024-10-10
JavaWeb開(kāi)發(fā)之【Tomcat 環(huán)境配置】MyEclipse+IDEA配置教程
這篇文章主要介紹了JavaWeb開(kāi)發(fā)之【Tomcat 環(huán)境配置】MyEclipse+IDEA配置教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
SpringBoot定制JSON響應(yīng)數(shù)據(jù)的實(shí)現(xiàn)
本文主要介紹了SpringBoot定制JSON響應(yīng)數(shù)據(jù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02
SpringMVC實(shí)現(xiàn)通過(guò)郵件找回密碼功能
本篇文章主要介紹的是SpringMVC實(shí)現(xiàn)通過(guò)郵件找回密碼功能,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧。2016-10-10
JavaSE圖像驗(yàn)證碼簡(jiǎn)單識(shí)別程序詳解
這篇文章主要為大家詳細(xì)介紹了JavaSE圖像驗(yàn)證碼簡(jiǎn)單識(shí)別程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
Springboot集成minio實(shí)現(xiàn)文件存儲(chǔ)的實(shí)現(xiàn)代碼
MinIO?是一款基于Go語(yǔ)言的高性能對(duì)象存儲(chǔ)服務(wù),本文主要介紹了Springboot集成minio實(shí)現(xiàn)文件存儲(chǔ)的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

