DelayQueue延迟队列
基本使用
- DelayQueue存储的元素必须实现Delayed接口
- 重写getDelay和compareTo方法
- getDelay返回小于等于0时候弹出元素
- compareTo方法用于堆中元素移动时候的比较
public class DelayQueueTest {
public static void main(String[] args) throws InterruptedException {
Task taskA = new Task("taskA", 6000L);
Task taskB = new Task("taskB", 3000L);
Task taskC = new Task("taskC", 4000L);
Task taskD = new Task("taskD", 5000L);
DelayQueue<Task> queue = new DelayQueue<>();
queue.put(taskA);
queue.put(taskB);
queue.put(taskC);
queue.put(taskD);
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
}
static class Task implements Delayed{
//任务名称
private String name;
//任务执行时间
private Long time;
public Task(String name, Long delay) {
this.name = name;
this.time = TimeUnit.MILLISECONDS.convert(System.currentTimeMillis() + delay,TimeUnit.MILLISECONDS);
}
@Override
public long getDelay(TimeUnit unit) {
return time - System.currentTimeMillis();
}
@Override
public int compareTo(Delayed o) {
return (int) (this.time - ((Task)o).getTime());
}
public String getName() {
return name;
}
public Long getTime() {
return time;
}
@Override
public String toString() {
return "Task{" +
"name='" + name + '\'' +
", time=" + time +
'}';
}
}
}
源码分析
成员变量
//锁,添加、移除元素公用一把锁
private final transient ReentrantLock lock = new ReentrantLock();
//存储元素使用优先级队列
private final PriorityQueue<E> q = new PriorityQueue<E>();
//存储正在等待元素的线程
private Thread leader;
//用于线程挂起、唤醒
private final Condition available = lock.newCondition();
构造方法
public DelayQueue() {}
public DelayQueue(Collection<? extends E> c) {
this.addAll(c);
}
添加元素相关方法
add(E e)
调用offer方法
public boolean add(E e) {
return offer(e);
}
add(E e)
调用offer方法
public void put(E e) {
offer(e);
}
offer(E e, long timeout, TimeUnit unit)
延迟队列是基于优先级队列实现,添加元素不存在阻塞情况,直接调用offer方法
public boolean offer(E e, long timeout, TimeUnit unit) {
return offer(e);
}
offer(E e)
offer方法分析
public boolean offer(E e) {
//拿到锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//调用优先级队列的offer方法弹出堆顶元素
q.offer(e);
//如果不存在元素,返回null
if (q.peek() == e) {
leader = null;
//这里是唤醒等待获取元素的线程
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
获取元素相关方法
remove(Object o)
调用优先级队列中的remove方法,返回堆顶元素
public boolean remove(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return q.remove(o);
} finally {
lock.unlock();
}
}
poll()
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//拿到堆顶元素
E first = q.peek();
//如果堆顶元素为null或者堆顶元素的延迟时间未到返回空,否则调用priorityQueue中的poll方法返回堆顶元素
return (first == null || first.getDelay(NANOSECONDS) > 0)
? null
: q.poll();
} finally {
lock.unlock();
}
}
poll(long timeout, TimeUnit unit)
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 <= 0L)
return null;
else
//否则挂起线程,等待生产者唤醒,即offer中的唤醒操作
nanos = available.awaitNanos(nanos);
} else {
//到这儿,证明有元素,拿到堆顶元素距离可以获取的时间,纳秒值
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0L)
//如果元素时间到了,直接返回
return q.poll();
//如果指定等待时间到了,返回null
if (nanos <= 0L)
return null;
//防止线程挂起后持有对象引用
first = null; // don't retain ref while waiting
//如果指定等待时间小于元素延迟时间或者有线程等待获取栈顶元素,挂起线程
if (nanos < delay || leader != null)
nanos = available.awaitNanos(nanos);
else {
//到这儿,证明没有线程等待获取堆顶元素且指定等待时间比元素时间大,在等待时间内可以获取元素
//否则将正在等待获取元素的线程设为当前线程
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
//线程挂起时间设置为元素的延迟时间
//注意:当前线程虽然阻塞时间为元素延迟时间,但当前线程不一定能拿到堆顶元素,因为阻塞结束会将leader设置为null,另外生产者如果发现堆顶元素变了也可以将lead设置为null,这会导致leader被设置为其它线程
long timeLeft = available.awaitNanos(delay);
nanos -= delay - timeLeft;
} finally {
//判断是否是等待元素的线程,如果是,将等待线程设为null
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
//每个线程到这需要判断有没有线程在等待元素,如果没有且元素不为null,唤醒等待的线程
if (leader == null && q.peek() != null)
//唤醒
available.signal();
lock.unlock();
}
}
take()
除非线程被打断,否则一直等到获取元素
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//死循环,直到拿到元素
for (;;) {
//拿到堆顶元素
E first = q.peek();
//如果为null,挂起线程,等待被生产者唤醒
if (first == null)
available.await();
else {
//元素不为null,拿到堆顶元素的延迟时间
long delay = first.getDelay(NANOSECONDS);
//如果元素延迟时间到了,直接返回
if (delay <= 0L)
return q.poll();
first = null; // don't retain ref while waiting
//如果元素延迟时间没到,且有其它线程正在等待元素,挂起线程
if (leader != null)
available.await();
else {
//如果没有线程等待元素,将当前线程设置为正等待元素的线程
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
//挂起线程时间设置为元素延迟时间
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}