DelayQueue源码
DelayQueue是一个支持延时获取元素的无界阻塞队列。队列使用PriorityQueue来实现。队 列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。 只有在延迟期满时才能从队列中提取元素
public class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements BlockingQueue<E> {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
private Thread leader = null;
private final Condition available = lock.newCondition();
}
DelayQueue
内部使用PriorityQueue
作为阻塞队列,使用ReentrantLock
实现线程同步,并且使用了leader-fellower
设计模式,如果要加入到DelayQueue
,必须实现Delayed
接口
offer插入队列
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
//获取独占锁
lock.lock();
try {
//插入队列
q.offer(e);
//如果插入的e是队头元素,并且e即将过期,则通知其他线程进行take()操作
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
take队列取数据
等待过期的数据,移除并删除
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
//获取独占锁,允许被中断
lock.lockInterruptibly();
try {
for (;;) {
//取出但不删除队列中数据e
E first = q.peek();
//如果没有即将过期的数据,则等待,让出独占锁
if (first == null)
available.await();
//处理即将过期的元素
else {
//计算元素还有多少时间过期
long delay = first.getDelay(NANOSECONDS);
//如果已经到期,则出队列,并从队列中删除该元素
if (delay <= 0)
return q.poll();
first = null; // don't retain ref while waiting
//时间未到,并且leader不等于null,则等待。主要是采用了leader-fellower pattern有关
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
//时间未到,leader为空时,将leader设置成当前线程
leader = thisThread;
try {
//leader线程等待delay时间内挂起等待,等待之后让出leader,即leader=null
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
//队列中有数据,且leader为空,则需要唤醒一下condition available等待队列上的线程,重新竞争锁,然后从挂起处继续执行
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
了解Leader-fellower pattern
所有线程会有三种身份中的一种:leader和follower,以及一个干活中的状态:proccesser。它的基本原则就是,永远最多只有一个leader。而所有follower都在等待成为leader。线程池启动时会自动产生一个Leader负责等待网络IO事件,当有一个事件产生时,Leader线程首先通知一个Follower线程将其提拔为新的Leader,然后自己就去干活了,去处理这个网络事件,处理完毕后加入Follower线程等待队列,等待下次成为Leader。这种方法可以增强CPU高速缓存相似性,及消除动态内存分配和线程间的数据交换
一个比较好的比喻 https://stackoverflow.com/questions/3058272/explain-leader-follower-pattern
DelayQueue
中Leader-fellower
模式
- 有多个线程操作take时,只有leader线程会生效
- 当
leader!=null
,其它的take线程均为follower,主要体现在available.await()
- 如果
leader == null
,就将当前线程设置成leader - 如果线程中队列元素到期,并且leader等于null,则唤醒其他操作take的线程,从中选出作为五个leader