DelayQueue如何在Java多线程并发开发中使用
更新时间:2023-11-151. DelayQueue类介绍
DelayQueue是Java中的一个并发容器,是一个无界阻塞队列(队列的长度可以动态的变化),队列中的元素必须实现Delayed接口,队列中的元素按照剩余延迟时间的大小进行排序,即最早要过期的元素放在队列头部,最晚要过期的元素放在队列尾部。队列头部元素只有在延迟期满时才能被获取到,可以等待延迟到期。DelayQueue对于需要延迟处理任务的场景是非常适用的。
public interface Delayed extends Comparable {
long getDelay(TimeUnit unit);
}
2. 使用DelayQueue实现延迟任务
在Java多线程开发中,我们可以通过DelayQueue来实现延迟任务。下面是一个简单的例子,演示如何使用DelayQueue实现延迟任务的调度。
public class DelayedTask implements Delayed {
private String name;
private Long delayTime;
private Long executeTime;
public DelayedTask(String name, Long delayTime) {
this.name = name;
this.delayTime = delayTime;
this.executeTime = System.currentTimeMillis() + delayTime;
}
@Override
public int compareTo(Delayed o) {
return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.executeTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public String toString() {
return name + ": " + delayTime;
}
}
public class DelayQueueTest {
public static void main(String[] args) throws Exception {
DelayQueue delayQueue = new DelayQueue<>();
delayQueue.put(new DelayedTask("task1", 1000L));
delayQueue.put(new DelayedTask("task2", 3000L));
delayQueue.put(new DelayedTask("task3", 4000L));
while (!delayQueue.isEmpty()) {
DelayedTask task = delayQueue.take();
System.out.println("execute task: " + task);
}
}
}
在上面的例子中,我们定义了一个DelayedTask类实现了Delayed接口,并在DelayQueue中存储多个DelayedTask对象,每个DelayedTask对象都有自己的延迟执行时间,即在DelayQueue中会根据DelayedTask对象的延迟时间来排序。
3. 使用DelayQueue实现定时任务
除了可以使用DelayQueue实现延迟任务的调度,我们还可以通过DelayQueue来实现定时任务。下面是一个使用ScheduledExecutorService和DelayQueue实现定时任务的简单示例。
public class DelayedTask implements Delayed {
private String name;
private Long delayTime;
private Long executeTime;
public DelayedTask(String name, Long delayTime) {
this.name = name;
this.delayTime = delayTime;
this.executeTime = System.currentTimeMillis() + delayTime;
}
@Override
public int compareTo(Delayed o) {
return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.executeTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public String toString() {
return name + ": " + delayTime;
}
}
public class DelayQueueTest {
public static void main(String[] args) throws Exception {
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
DelayQueue delayQueue = new DelayQueue<>();
DelayedTask task1 = new DelayedTask("task1", 2000L);
DelayedTask task2 = new DelayedTask("task2", 3000L);
DelayedTask task3 = new DelayedTask("task3", 4000L);
delayQueue.put(task1);
delayQueue.put(task2);
delayQueue.put(task3);
executorService.scheduleAtFixedRate(() -> {
if (!delayQueue.isEmpty()) {
DelayedTask task = delayQueue.take();
System.out.println("execute task: " + task + " at " + new Date());
if (task.equals(task1)) {
delayQueue.put(new DelayedTask("task4", 2000L));
}
}
}, 0L, 1L, TimeUnit.SECONDS);
executorService.shutdown();
}
}
在上面的例子中,我们创建了一个ScheduledExecutorService,通过DelayQueue来存储定时任务。首先将定时任务放入DelayQueue,然后在ScheduledExecutorService中定时执行任务,如果DelayQueue中有已经到达执行时间的任务,就从DelayQueue中取出来执行,并将新的定时任务放入DelayQueue。
4. DelayQueue的注意事项
使用DelayQueue要注意以下几点:
- DelayQueue是一个无界队列,其大小在运行时动态调整。
- DelayQueue的元素必须实现Delayed接口。
- DelayQueue可以阻塞获取元素,也可以设置超时获取元素。
- 在使用DelayQueue进行延迟任务调度时,注意每个任务的执行时间不能太长,否则其它任务将会被阻塞。