任务调度之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接口
1
2
3
4
5
// 继承Comparable接口,表示该类对象支持排序
public interface Delayed extends Comparable<Delayed> {
    // 返回该对象剩余时延
    long getDelay(TimeUnit unit);
}
Delayed接口很简单,继承了Comparable接口,表示对象是可以比较排序的。ScheduledFuture接口
1
2
3
// 仅仅继承了Delayed和Future接口,自身没有任何代码
public interface ScheduledFuture<V> extends Delayed, Future<V> {
}

RunnableScheduledFuture接口

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

ScheduledFutureTask类

回到 schecule 方法中,它创建了一个ScheduledFutureTask的对象,由上面的关系图可知,ScheduledFutureTask直接或者间接实现了很多接口,ScheduledFutureTask实现方法:
构造方法
1
2
3
4
5
6
7
8
9
10
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接口的实现

1
2
3
4
// 实现Delayed接口的getDelay方法,返回任务开始执行的剩余时间
public long getDelay(TimeUnit unit) {
    return unit.convert(time - now(), TimeUnit.NANOSECONDS);
}
 
2、实例
指定时间给发送消息。将消息(包含发送时间)存储在数据库中,然后实现一个定时任务,每隔1秒检查数据库在当前时间有没有需要发送的消息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 @   jrliu  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示