DelayQueueソースコード分析
記事のディレクトリ
序文
パブリッククラスDelayQueueはAbstractQueueを拡張
し、BlockingQueueを実装します{
AbstractQueue:Queueの基本スケルトン。BlockingQueue
:ブロッキングキューインターフェイス
。Delayed:遅延インターフェイス。オブジェクトの残りの遅延時間を返すgetDelayedメソッドが含まれます。このインターフェイスはComparableを実装します。
ヒント:以下はこの記事の内容です。以下のケースは参照用です。
1つは、DelayQueueソースコード部分です。
1.コンストラクター
//无参空构造函数
public DelayQueue() {
}
//带参构造函数
public DelayQueue(Collection<? extends E> c) {
this.addAll(c);
}
//加入一个集合的元素到该队列中去
public boolean addAll(Collection<? extends E> c) {
if (c == null) //不能加入空元素
throw new NullPointerException(); //空指针异常
if (c == this)
throw new IllegalArgumentException(); //非法算术异常(不能自己加自己)
boolean modified = false;
for (E e : c) //遍历集合元素添加到队列中去
if (add(e)) //调用了本类的offer方法,成功时返回true
modified = true;
return modified; //只有队列被修改并且没有抛出异常的情况下将会返回true
}
2.メンバー変数
//该延迟队列底层是用的一个优先级队列来存储数据的
private final PriorityQueue<E> q = new PriorityQueue<E>();
//全局锁
private final transient ReentrantLock lock = new ReentrantLock();
//当队列头部有新元素可用或新线程可能需要成为leader时发出的条件。
private final Condition available = lock.newCondition();
///
//关于Condition中线程的挂起和唤醒的一些方法的使用
//Condition的创建
ReentrantLock lock = new ReentrantLock(); //首先需要一把锁
Condition condition = lock.newCondition(); //通过该锁实例对象创建Condition
//该方法阻塞当前线程直到被其他线程唤醒或者该线程被中断
condition.await();
//该方法阻塞当前线程直到被其他线程唤醒或者该线程被中断或者超过指定long时间(时间单位为纳秒)
condition.awaitNanos(long);
//唤醒阻塞线程中的一个
condition.signal();
//唤醒阻塞的所有线程
condition.signalAll();
//执行上面方法时必须先获得lock锁(lock.lock()),否则将抛出异常java.lang.IllegalMonitorStateException。
//当一个线程要从队列中取元素时,队首的元素可能剩余延迟时间>0此时,该线程需要
//等待剩余延迟时间然后才可以等待其他线程将其唤醒,此时其他线程也可从队列中获取元素,发现leader不为null的时候,其他线程将会直接阻塞等待唤醒
private Thread leader = null;
3.主な方法
チームに参加する
オファー方法
public boolean offer(E e) {
final ReentrantLock lock = this.lock; //获取全局锁
lock.lock(); //加锁
try {
q.offer(e); //向优先级队列加入元素e
//如果加入的元素是队列中延迟时间最小的(优先级最高的)该元素则位于队首
if (q.peek() == e) {
leader = null; //leader线程所指向线程将于其他线程有相等的竞争
available.signal(); //唤醒其他等待出队操作的线程(随机唤醒一个)
}
return true; //入队成功返回true
} finally {
lock.unlock(); //释放锁
}
}
//该方法与上面方法相同
public boolean offer(E e, long timeout, TimeUnit unit) {
return offer(e);
}
//该方法用于获得队首元素(加锁了)
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.peek();
} finally {
lock.unlock();
}
}
//与offer方法相同
public void put(E e) {
offer(e);
}
チームを離れる
public E poll() {
final ReentrantLock lock = this.lock; //全局锁
lock.lock(); //加锁
try {
E first = q.peek(); //获得优先级队列中的队首元素(不移除该元素)
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;//队列为null,或有剩余延迟时间,直接返回空
else
return q.poll(); //出队
} finally {
lock.unlock(); //释放锁
}
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout); //超时时间
final ReentrantLock lock = this.lock; //获取全局锁
lock.lockInterruptibly(); //加锁线程中断时该锁可被打破释放
try {
for (;;) {
E first = q.peek(); //返回队首元素
if (first == null) {
//队首为null则队列为空
if (nanos <= 0) //等待时间<=0直接返回null
return null;
else //否则该线程阻塞nanos时间后自动被唤醒或中途被其他线程唤醒
nanos = available.awaitNanos(nanos);
} else {
//队列非空的情况
long delay = first.getDelay(NANOSECONDS); //获取队首元素剩余延迟时间
if (delay <= 0) //剩余延迟时间<= 0
return q.poll(); //直接出队poll过程 //a
if (nanos <= 0) //剩余延迟时间>0并且超时时间 <= 0的情况下(该线程不等待)
return null; //直接返回null //b
first = null; // don't retain ref while waiting
//1.这里当超时时间小于延迟时间时,该线程仍然等待nanos时间,因
//为此时他释放了自己锁持有的锁资源,其他线程可以进行入队操作,
//且队首元素可能被改变
//2.如果当超时时间大于等于延迟时间时,并且leader不为null(说明
//也有线程在进行出队操作),将会等待nanos时间
if (nanos < delay || leader != null)
nanos = available.awaitNanos(nanos);
else {
//3. 没有其他线程在进行入队操作时
Thread thisThread = Thread.currentThread(); //获取当前线程
leader = thisThread; //设置成员变量leader为当前线程
try {
long timeLeft = available.awaitNanos(delay); //等待指定延迟时间
//这里等待指定延迟时间后为什么不直接去得队首元素
//和上面原因类似,此时其他线程可能进行入队或出队操作改变了队首元素
nanos -= delay - timeLeft; //更新超时时间
} finally {
//1.这里leader可能为空因为在当前线程阻塞的时候,其他入队操作的线程可能将leader赋值为null,leader为null说明 当前线程是被其他入队操作线程唤醒的
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
//队列中还有元素,并且不存在leader线程了
if (leader == null && q.peek() != null)
available.signal(); //唤醒其他阻塞的在出队上的线程
lock.unlock(); //释放锁
}
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock; //全局锁
lock.lockInterruptibly(); //可中断加锁
try {
for (;;) {
E first = q.peek(); //获取队首元素不移除
if (first == null) //队列为null
available.await(); //阻塞当前线程等待被唤醒
else {
//队列中存在元素的情况
long delay = first.getDelay(NANOSECONDS); //获取延迟时间
if (delay <= 0) //元素已过期
return q.poll(); //直接返回该元素
first = null; // don't retain ref while waiting
if (leader != null) //队首元素的剩余延迟时间大于0,有其他线程也在出队操作
available.await(); //直接阻塞当前线程
else {
//队首为未过期元素并且当前没有其他线程出队阻塞
Thread thisThread = Thread.currentThread();
leader = thisThread; //将当前线程设置为leader
try {
available.awaitNanos(delay); //阻塞队首延迟时间后被唤醒
} finally {
//leader可能为null,因为其他线程的入队操作导致的
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
//队列中还含有其他元素并且leader为null
if (leader == null && q.peek() != null)
available.signal(); //唤醒其他阻塞在出队操作上的线程
lock.unlock(); //锁释放
}
}
総括する
- DelayQueueは遅延キューであり、キュー内の要素はDelayedインターフェイスを実装する必要があります。
- キューの最下層は、完全にバイナリヒープ優先度の無制限キューを使用します
- DelayQueueキューはBlockingQueueを実装します。これは、キューのブロック操作をデキューできます(グローバルロックを使用しながらデキューして操作を開始します)
:キューがnullの場合、現在のスレッドをブロックし、他のスレッドがウェイクアップするのを待ちます。キューがnullでない場合は、キューの先頭にある要素の有効期限が切れているかどうかを判断します。有効期限が切れた直後に返されます。リーダーがnullの場合、スレッドが時間だけブロックされているか、他のスレッドを待機してブロックされているかが判断されます。ウェイクアップするスレッド。pollは、パラメータなしでキューに期限切れの要素がある場合に直接返します。それ以外の場合はnullを返します。パラメータを取得する場合、タイムアウト期間(スレッドが待機できる時間)が追加されることを除いて、取得と同様です。タイムアウト期間を超えると、nullが直接返されます。
チームに参加する:オファー、プット(オファーは直接呼び出されます)、チームに正常に参加したときに直接trueを返します。 - ブロッキングキューにはメンバー変数リーダースレッドが含まれています。複数のスレッドがテイクを取得している場合、指定された時間待機した後に各スレッドを自動的にウェイクアップする必要はありません。最初のテイクスレッドのみがそのような処理を実行し、後続のすべてのテイクスレッドが必要です。リーダーが空であるかどうかを判別して、スレッドが直接待機しているかどうかを判別します。(awaitNonasメソッドのスピン判定は非効率的です)。
- DelayQueueには、スレッド間のウェイクアップとブロッキングを処理するための条件変数があります。関連するメソッドを使用するには、関連するロックを最初に取得する必要があり、スレッドは、待機または待機時に関連するロックを最初に取得するか、現在のすべてのロックリソースを解放する必要があります。そして、それが再び目覚めたとき、再び獲得のために競争します。
遅延インターフェース
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);
}