# 并发队列-无界阻塞延迟队列DelayQueue原理探究

0.282字数 956阅读 6611

# 一、前言

DelayQueue队列中每个元素都有个过期时间，并且队列是个优先级队列，当从队列获取元素时候，只有过期元素才会出队列。

image.png

# 三、offer操作

``````
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
q.offer(e);
if (q.peek() == e) {（2）
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
``````

# 四、take操作

``````
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
//获取但不移除队首元素（1)
E first = q.peek();
if (first == null)
available.await();//(2)
else {
long delay = first.getDelay(TimeUnit.NANOSECONDS);
if (delay <= 0)//(3)
return q.poll();
available.await();
else {
try {
available.awaitNanos(delay);
} finally {
}
}
}
}
} finally {
if (leader == null && q.peek() != null)//(6)
available.signal();
lock.unlock();
}
}
``````

# 五、poll操作

``````    public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
//如果队列为空，或者不为空但是队头元素没有过期则返回null
if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
return null;
else
return q.poll();
} finally {
lock.unlock();
}
}
``````

# 六、一个例子

``````class DelayedEle implements Delayed {

private final long delayTime; //延迟时间
private final long expire;  //到期时间
private String data;   //数据

public DelayedEle(long delay, String data) {
delayTime = delay;
this.data = data;
expire = System.currentTimeMillis() + delay;
}

/**
* 剩余时间=到期时间-当前时间
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire - System.currentTimeMillis() , TimeUnit.MILLISECONDS);
}

/**
* 优先队列里面优先级规则
*/
@Override
public int compareTo(Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS) -o.getDelay(TimeUnit.MILLISECONDS));
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("DelayedElement{");
sb.append("delay=").append(delayTime);
sb.append(", expire=").append(expire);
sb.append(", data='").append(data).append('\'');
sb.append('}');
return sb.toString();
}
}
``````
``````
public static void main(String[] args) {

DelayQueue<DelayedEle> delayQueue = new DelayQueue<DelayedEle>();

DelayedEle element1 = new DelayedEle(1000,"zlx");
DelayedEle element2 = new DelayedEle(1000,"gh");

delayQueue.offer(element1);
delayQueue.offer(element2);

element1 =  delayQueue.take();
System.out.println(element1);

}
``````

# 七、使用场景

• 一个典型场景是，重试机制实现，比如当调用接口失败后，把当前调用信息放入delay=10s的元素，然后把元素放入队列，那么这个队列就是一个重试队列，一个线程通过take方法获取需要重试的接口，take返回则接口进行重试，失败则再次放入队列，同时也可以在元素加上重试次数。

• TimerQueue的内部实现

`欢迎看官们拍砖，让我们共同进步！`