学会如何使用延迟队列

一个例子包教会使用DelayQueue

Posted by Jeremy Song on 2022-06-26
Estimated Reading Time 2 Minutes
Words 683 In Total
Viewed Times

场景

通常在我们需要实现一个顺序执行的管理器时,会使用一个队列来完成。

队列的数据结构决定了,只有前一个任务被执行消费掉,才能轮到下一个任务。其分配到资源就可以执行自身的业务了。
但是一般情况下,队列都是执行完一个之后就紧接着执行下一个了,中间是没有停顿的。例如java的 ArrayDeque<E>PriorityQueue<E> 等JDK的默认实现。

那么,如果有一个场景是需要在某个任务执行完后一个指定的时间才能执行下一个任务呢?

一种方法就是在主线程中或控制线程中通过 Thread.sleep(long millis) 方法来暂停程序,等待满足指定的时间再执行下一个。
另一种方法这是使用延迟队(DelayQueue)列来帮我们完成这个控制。

延迟队列的使用

下面的代码简单的展示了如何使用延迟队列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.util.Queue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class Test {
public static void main(String[] args) {
Queue<DelayInt> queue = new DelayQueue<>();
for (int i = 0; i < 10; i++) {
queue.offer(new DelayInt(i, 2 * i));
}

while (!queue.isEmpty()) {
DelayInt curr = queue.poll();
if (curr == null) {
continue;
}
System.out.println(curr.value);
}
}

static private class DelayInt implements Delayed {
public DelayInt(int value, int delay) {
this.value = value;
this.delay = delay;
this.currentTimeMillis = System.currentTimeMillis();
}

private final long currentTimeMillis;
private final int value;
private final int delay;
private final TimeUnit unit = TimeUnit.SECONDS;

@Override
public long getDelay(TimeUnit unit) {
long duration = currentTimeMillis + delay * 1000L - System.currentTimeMillis();
return unit.convert(duration, this.unit);
}

@Override
public int compareTo(Delayed o) {
return (int) (this.getDelay(this.unit) - o.getDelay(this.unit));
}
}
}

使用延迟队列需要注意的地方

  • 延迟队列的每个Item需要实现 java.util.concurrent.Delayed 接口
    • 该接口只有一个方法long getDelay(TimeUnit unit),表示指定时间单位的还剩余的延迟时间
    • 实现接口的时候需要根据实际情况考虑时间单位转换
    • 另外还需要顺便实现 Delayed 接口继承的 Comparable<Delayed> 接口
      • 实现 compareTo 方法会给队列排序
  • 延迟队列获取队列头部元素有两个方法:
    • public E poll()
      • 这个方法直接获取队列头部元素,如果当前有已经满足延迟时间的元素,则返回第一个。反之,返回 null
    • public E poll(long timeout, TimeUnit unit) throws InterruptedException
      • 这个方法给定一个时间,如果不能立即获取元素,则会调用其成员ConditionawaitNanos(long nanosTimeout)方法
      • 该方法可能会阻塞当前进程

欢迎关注我的公众号 须弥零一,跟我一起学习IT知识。


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !