Java 线程池体系 - ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor

推理: 由于ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,故拥有其特性及功能,但是目前想要实现调度功能,需要加入自己的逻辑实现
需求 : 需要进行任务调度且可以延迟执行
结论 : 需要能够对任务进行时间排队的任务队列

问题 : 如何做到调度与延迟?

  • DelayedWorkQueue 延迟工作队列

1. 构造方法

  • 默认构造方法
  public ScheduledThreadPoolExecutor(int corePoolSize) {
        //调用父类构造实现创建
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
  }
  • 图解

构造方法

  • 描述
    • 由于ScheduleThreadPoolExecutor使用了父类ThreadPoolExecutor的构造方法,传参中的任务队列为当前内部类,且并无初始化大小及DelayWorkQueue的构造方法,所以DelayedWorkQueue为无界队列,即没有容量限制
    • 由于非核心线程的创建的前提条件是核心线程数与任务队列满载,所以ScheduleThreadExecutor的构造函数中除去核心线程数及任务队列其他参数无效

2. 执行方式

/**
* @param Runnable command 待执行任务
* @param long delay 延迟时间
* @param TimeUnit unit delay的时间单位
*/
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
    if (command == null || unit == null){
        throw new NullPointerException();
    }

	//hock钩子函数,用于扩展
    //decorateTask()方法默认实现为空,仅返回传入的任务,留给用户重写,如字面意思,可以对任务进行操作加工等
    //ScheduledFutureTask类,解析如下图及代码详解
    RunnableScheduledFuture<?> t = decorateTask(command,new ScheduledFutureTask<Void>(command, null,triggerTime(delay, unit)));
    //执行延时任务方法
    delayedExecute(t);
    //返回任务
    return t;
}
  • 相关图解

接口继承

  • 描述

    • 由于接口在设计过程中需要满足单一原则,即接口隔离,如果想同时拥有两个或者两个以上接口的能力,那么需要继承对应接口,从而完成接口(能力)的组合
  • ScheduledFutureTask 代码详解

private class ScheduledFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> {

        //序列号,依据任务放入顺序严格递增
        private final long sequenceNumber;

        //超时时间,绝对时间
        private long time;

        //周期,每个多长时间执行一次,即 下次任务执行时间 = 绝对时间 + 周期
        private final long period;

        RunnableScheduledFuture<V> outerTask = this;

        //索引,任务的存放是以小顶堆(二叉树)的形式存储,所以,该下标即是表示的当前任务在数组中的位置
        int heapIndex

        //直接使用父类构造创建一次性任务
        ScheduledFutureTask(Runnable r, V result, long ns) {
            this(r, result,ns,0);
        }

        //基于父类构造方法基础上创建周期性任务
        ScheduledFutureTask(Runnable r, V result, long ns, long period) {
            super(r, result);
            this.time = ns;
            this.period = period;
            //利用AtomicLong原子类保证序列号的正常使用,即保证多线程下序列号的安全使用
            this.sequenceNumber = sequencer.getAndIncrement();
        }

        //基于父类构造的Callable方式创建一次性任务
        ScheduledFutureTask(Callable<V> callable, long ns) {
            super(callable);
            this.time = ns;
            this.period = 0;
            this.sequenceNumber = sequencer.getAndIncrement();
        }

		//获取任务的剩余延迟时间,剩余时间 = 任务开始时间 - 当前时间; (纳秒)
        public long getDelay(TimeUnit unit) {
            return unit.convert(time - now(), NANOSECONDS);
        }

		//由于RunnableScheduledFuture继承ScheduledFuture继承Delayed继承Comparable故重写方法以便比较
        public int compareTo(Delayed other) {
            if (other == this) // compare zero if same object
                return 0;
            if (other instanceof ScheduledFutureTask) {
                ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
                long diff = time - x.time;
                if (diff < 0)
                    return -1;
                else if (diff > 0)
                    return 1;
                else if (sequenceNumber < x.sequenceNumber)
                    return -1;
                else
                    return 1;
            }
            long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
            return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
        }

		//是否为周期性任务判断
        public boolean isPeriodic() {
            return period != 0;
        }

        //设置下一次运行定期任务的时间。(详解见文章下面setNextRunTime()方法图解)
        private void setNextRunTime() {
            long p = period;
            if (p > 0)
            	//schedule相关,
                time += p;
            else{
            	//scheduleWithFixedDelay相关
                time = triggerTime(-p);
            }
        }

        public boolean cancel(boolean mayInterruptIfRunning) {
            boolean cancelled = super.cancel(mayInterruptIfRunning);
            if (cancelled && removeOnCancel && heapIndex >= 0)
                remove(this);
            return cancelled;
        }


        public void run() {
            boolean periodic = isPeriodic();
            //状态检查,判断是否可以在当前状态下执行,防止线程池状态变更
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            //如果不是周期性任务,则直接使用父类run()    
            else if (!periodic)
                ScheduledFutureTask.super.run();
            //如果是周期性任务,运行并且重置任务    
            else if (ScheduledFutureTask.super.runAndReset()) {
                setNextRunTime();
                //定期重新执行任务(详解见下方方法)
                reExecutePeriodic(outerTask);
            }
        }
    }
  • setNextRunTime() 方法详解
   private void setNextRunTime() {
      long p = period;
      if (p > 0){ time += p; }  //下次执行时间 = 当前时间 + 延迟时间
      else{ time = triggerTime(-p); } //下次执行时间 = 当前时间 + 任务消耗时间 + 延迟时间
 }

方法详细图解

  • reExecutePeriodic() →→→ 任务定期重新执行
    void reExecutePeriodic(RunnableScheduledFuture<?> task) {
    	//判断线程池状态
        if (canRunInCurrentRunState(true)) {
            super.getQueue().add(task);
            //判断线程池状态,如果不满足运行可运行条件,则删除本次任务
            if (!canRunInCurrentRunState(true) && remove(task))
                task.cancel(false);
            else
            	//保证线程池至少有一个线程
                ensurePrestart();
        }
    }

频繁的判断线程池状态,是由于多线程状态下,线程池的运行随时可能被其他线程锁中断

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值