任务调度之ScheduledThreadPoolExecutor

1、前言
 
ScheduledExecutorService在普通执行器接口(ExecutorService)的基础上引入了Future模式,使得可以限时或周期性地调度任务。
ScheduledThreadPoolExecutor其实是继承了ThreadPoolExecutor这个普通线程池,同时实现ScheduledThreadPoolExecutorService接口。ThreadPoolExecutor中提交的任务都是实现了Runnable接口,但是ScheduledThreadPoolExecutor比较特殊,由于要满足任务的延迟/周期调度功能,它会对所有的Runnable任务都进行包装,包装成一个RunnableScheduledFuture任务。
RunnableScheduledFuture是Future模式中的一个接口,在ThreadPoolExecutor中,需要指定一个阻塞队列作为任务队列。ScheduledThreadPoolExecutor中也一样,不过特殊的是,ScheduledThreadPoolExecutor中的任务队列是一种特殊的延时队列(DelayQueue)。ScheduledThreadPoolExecutor在内部定义了DelayQueue的变种——DelayedWorkQueue,它和DelayQueue类似,只不过要求所有入队元素必须实现RunnableScheduledFuture接口。
 
Delayed接口
// 继承Comparable接口,表示该类对象支持排序
public interface Delayed extends Comparable<Delayed> {
    // 返回该对象剩余时延
    long getDelay(TimeUnit unit);
}
Delayed接口很简单,继承了Comparable接口,表示对象是可以比较排序的。ScheduledFuture接口
// 仅仅继承了Delayed和Future接口,自身没有任何代码
public interface ScheduledFuture<V> extends Delayed, Future<V> {
}

RunnableScheduledFuture接口

public interface RunnableScheduledFuture<V> extends RunnableFuture<V>, Schedule
    // 是否是周期任务,周期任务可被调度运行多次,非周期任务只被运行一次
    boolean isPeriodic();
}

ScheduledFutureTask类

回到 schecule 方法中,它创建了一个ScheduledFutureTask的对象,由上面的关系图可知,ScheduledFutureTask直接或者间接实现了很多接口,ScheduledFutureTask实现方法:
构造方法
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
    // 调父类FutureTask的构造方法
    super(r, result);
    // time表示任务下次执行的时间
    this.time = ns;
    // 周期任务,正数表示按照固定速率,负数表示按照固定时延,0表示不是周期任务
    this.period = period;
    // 任务的编号
    this.sequenceNumber = sequencer.getAndIncrement();
}

Delayed接口的实现

// 实现Delayed接口的getDelay方法,返回任务开始执行的剩余时间
public long getDelay(TimeUnit unit) {
    return unit.convert(time - now(), TimeUnit.NANOSECONDS);
}
 
2、实例
指定时间给发送消息。将消息(包含发送时间)存储在数据库中,然后实现一个定时任务,每隔1秒检查数据库在当前时间有没有需要发送的消息。
public class ScheduledTask {
    private static final ScheduledExecutorService executor = new  ScheduledThreadPoolExecutor(1, Executors.defaultThreadFactory());
    private static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static void main(String[] args){
        // 新建一个固定延迟时间的计划任务
        executor.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                if (haveMsgAtCurrentTime()) {
                    System.out.println(df.format(new Date()));
                    System.out.println("大家注意了,我要发消息了");
                }
            }
        }, 1, 1, TimeUnit.SECONDS);
    }
    public static boolean haveMsgAtCurrentTime(){
         //查询数据库,有没有当前时间需要发送的消息
        // 这⾥省略实现,直接返回true
        return true;
    }
}

 

 

 
posted @ 2023-03-27 23:39  jrliu  阅读(33)  评论(0编辑  收藏  举报