c语言编程笔录

首页 >   > 笔记大全

笔记大全

DelayQueue如何在Java多线程并发开发中使用

更新时间:2023-11-15

1. 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进行延迟任务调度时,注意每个任务的执行时间不能太长,否则其它任务将会被阻塞。