java加载c库阻塞_【死磕Java並發】-----J.U.C之阻塞隊列:DelayQueue
DelayQueue是一個支持延時獲取元素的無界阻塞隊列。里面的元素全部都是“可延期”的元素,列頭的元素是最先“到期”的元素,如果隊列里面沒有元素到期,是不能從列頭獲取元素的,哪怕有元素也不行。也就是說只有在延遲期到時才能夠從隊列中取元素。
DelayQueue主要用於兩個方面:
- 緩存:清掉緩存中超時的緩存數(shù)據(jù)
- 任務(wù)超時處理
DelayQueue
DelayQueue實現(xiàn)的關(guān)鍵主要有如下幾個:
可重入鎖ReentrantLock
用於阻塞和通知的Condition對象
根據(jù)Delay時間排序的優(yōu)先級隊列:PriorityQueue
用於優(yōu)化阻塞通知的線程元素leader
ReentrantLock、Condition這兩個對象就不需要闡述了,他是實現(xiàn)整個BlockingQueue的核心。PriorityQueue是一個支持優(yōu)先級線程排序的隊列(參考【死磕Java並發(fā)】—–J.U.C之阻塞隊列:PriorityBlockingQueue),leader后面闡述。這里我們先來了解Delay,他是實現(xiàn)延時操作的關(guān)鍵。
Delayed
Delayed接口是用來標(biāo)記那些應(yīng)該在給定延遲時間之后執(zhí)行的對象,它定義了一個long getDelay(TimeUnit unit)方法,該方法返回與此對象相關(guān)的的剩余時間。同時實現(xiàn)該接口的對象必須定義一個compareTo 方法,該方法提供與此接口的 getDelay 方法一致的排序。
public interface Delayed extends Comparable {
long getDelay(TimeUnit unit);
}
如何使用該接口呢?上面說的非常清楚了,實現(xiàn)該接口的getDelay()方法,同時定義compareTo()方法即可。
內(nèi)部結(jié)構(gòu)
先看DelayQueue的定義:
public class DelayQueue extends AbstractQueue
implements BlockingQueue {
/** 可重入鎖 */
private final transient ReentrantLock lock = new ReentrantLock();
/** 支持優(yōu)先級的BlockingQueue */
private final PriorityQueue q = new PriorityQueue();
/** 用於優(yōu)化阻塞 */
private Thread leader = null;
/** Condition */
private final Condition available = lock.newCondition();
/**
* 省略很多代碼
*/
}
看了DelayQueue的內(nèi)部結(jié)構(gòu)就對上面幾個關(guān)鍵點一目了然了,但是這里有一點需要注意,DelayQueue的元素都必須繼承Delayed接口。同時也可以從這里初步理清楚DelayQueue內(nèi)部實現(xiàn)的機(jī)制了:以支持優(yōu)先級無界隊列的PriorityQueue作為一個容器,容器里面的元素都應(yīng)該實現(xiàn)Delayed接口,在每次往優(yōu)先級隊列中添加元素時以元素的過期時間作為排序條件,最先過期的元素放在優(yōu)先級最高。
offer()
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 向 PriorityQueue中插入元素
q.offer(e);
// 如果當(dāng)前元素的對首元素(優(yōu)先級最高),leader設(shè)置為空,喚醒所有等待線程
if (q.peek() == e) {
leader = null;
available.signal();
}
// 無界隊列,永遠(yuǎn)返回true
return true;
} finally {
lock.unlock();
}
}
offer(E e)就是往PriorityQueue中添加元素,具體可以參考(【死磕Java並發(fā)】—–J.U.C之阻塞隊列:PriorityBlockingQueue)。整個過程還是比較簡單,但是在判斷當(dāng)前元素是否為對首元素,如果是的話則設(shè)置leader=null,這是非常關(guān)鍵的一個步驟,后面闡述。
take()
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
// 對首元素
E first = q.peek();
// 對首為空,阻塞,等待off()操作喚醒
if (first == null)
available.await();
else {
// 獲取對首元素的超時時間
long delay = first.getDelay(NANOSECONDS);
// <=0 表示已過期,出對,return
if (delay <= 0)
return q.poll();
first = null; // don't retain ref while waiting
// leader != null 證明有其他線程在操作,阻塞
if (leader != null)
available.await();
else {
// 否則將leader 設(shè)置為當(dāng)前線程,獨占
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
// 超時阻塞
available.awaitNanos(delay);
} finally {
// 釋放leader
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
// 喚醒阻塞線程
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
首先是獲取對首元素,如果對首元素的延時時間 delay <= 0 ,則可以出對了,直接return即可。否則設(shè)置first = null,這里設(shè)置為null的主要目的是為了避免內(nèi)存泄漏。如果 leader != null 則表示當(dāng)前有線程占用,則阻塞,否則設(shè)置leader為當(dāng)前線程,然后調(diào)用awaitNanos()方法超時等待。
first = null
這里為什么如果不設(shè)置first = null,則會引起內(nèi)存泄漏呢?線程A到達(dá),列首元素沒有到期,設(shè)置leader = 線程A,這是線程B來了因為leader != null,則會阻塞,線程C一樣。假如線程阻塞完畢了,獲取列首元素成功,出列。這個時候列首元素應(yīng)該會被回收掉,但是問題是它還被線程B、線程C持有著,所以不會回收,這里只有兩個線程,如果有線程D、線程E…呢?這樣會無限期的不能回收,就會造成內(nèi)存泄漏。
這個入隊、出對過程和其他的阻塞隊列沒有很大區(qū)別,無非是在出對的時候增加了一個到期時間的判斷。同時通過leader來減少不必要阻塞。
歡迎掃一掃我的公眾號關(guān)注 — 及時得到博客訂閱哦!
–— Java成神之路: 488391811(一起走向Java成神) –—
總結(jié)
以上是生活随笔為你收集整理的java加载c库阻塞_【死磕Java並發】-----J.U.C之阻塞隊列:DelayQueue的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 用户登录token_Java,
- 下一篇: php加图片源码_PHP添加文字水印或图