ScheduledThreadPoolExecutor阅读理解

ScheduledThreadPoolExecutor阅读笔记

一、简介

  • 指定某个时刻执行任务,是通过延时队列的特性来解决的
  • 重复执行,是通过在任务执行后再次把任务加入到队列来解决的

二、继承关系图

在这里插入图片描述

三、存储结构

  • 自己内部封装了一个ScheduledFutureTask内部类,用于封装FutureTask的
  • 自己内部封装了一个DelayedWorkQueue内部类,延迟阻塞队列,用于等待线程

四、源码分析

内部类
  • ScheduledFutureTask内部类继承图

在这里插入图片描述

  • ScheduledFutureTask 内部类主要方法

    public void run() {
        // 是否重复执行
        boolean periodic = isPeriodic();
        // 检查线程池状态,如果状态不可运行,就取消
        if (!canRunInCurrentRunState(periodic))
            cancel(false);
        // 如果不是重复运行,也就是一次任务,则直接调用父类FutureTask.run()方法
        else if (!periodic)
            ScheduledFutureTask.super.run();
        //
        else if (ScheduledFutureTask.super.runAndReset()) {
            // 设置下次执行的时间,也就是修改period属性
            setNextRunTime();
            // 重复执行,直接主类
            reExecutePeriodic(outerTask);
        }
    }
    
    //ScheduledThreadPoolExecutor#reExecutorPeriodic 重复执行
    void reExecutePeriodic(RunnableScheduledFuture<?> task) {
        // 调用重复执行,那么肯定是重复任务
        // 检查线程池状态
        if (canRunInCurrentRunState(true)) {
            // 再次把任务扔到任务队列中
            super.getQueue().add(task);
            // 再次检查线程池状态,
            // 如果状态不可用就从队列删除任务,删除成功后取消线程(不是强制取消)
            if (!canRunInCurrentRunState(true) && remove(task))
                task.cancel(false);
            else
                // 保证工作线程足够
                ensurePrestart();
        }
    }
    
  • DelayedWorkQueue内部类继承关系图
    在这里插入图片描述

  • DelayedWorkQueue 内部类主要函数

    • take

      public RunnableScheduledFuture<?> take() throws InterruptedException {
          final ReentrantLock lock = this.lock;
          // 加锁,如果线程终端抛线程中断异常
          lock.lockInterruptibly();
          try {
              for (;;) {
                  // 堆顶任务
                  RunnableScheduledFuture<?> first = queue[0];
                  // 如果队列为空,则等待
                  if (first == null)
                      available.await();
                  else {
                      // 查看堆顶任务还有多久到时间
                      long delay = first.getDelay(NANOSECONDS);
                      // 如果小于等于0,说明这个任务到时间了,可以从队列出队了
                      if (delay <= 0)
                          // 出队,然后堆化
                          return finishPoll(first);
                      // 还没有到时间
                      first = null; // don't retain ref while waiting
                      // 如果前面有线程在等待,直接进入等待
                      if (leader != null)
                          available.await();
                      else {
                          // 前面没有等待,直接把当前线程做为leader
                          Thread thisThread = Thread.currentThread();
                          leader = thisThread;
                          try {
                              // 等待上面计算的延时时间,再自动唤醒
                              available.awaitNanos(delay);
                          } finally {
                              // 唤醒后再次获得锁后把leader置空(不然重新循环会加入队列)
                              if (leader == thisThread)
                                  leader = null;
                          }
                      }
                  }
              }
          } finally {
              if (leader == null && queue[0] != null)
                  // 相当于唤醒下一个等待的任务
                  available.signal();
              // 释放锁
              lock.unlock();
          }
      }
      
属性
构造
  • 其实就是用的ThreadPoolExecutor的构造,只是等待队列采用的自己的DelayedWorkDueue内部类
主要方法
  • ScheduleAtFixedRate

  • 处理和未来任务一样,只是会装饰成另外一个任务再去执行

    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        // 参数效验
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0)
            throw new IllegalArgumentException();
        // 将普通任务砖石成ScheduledFutureTask内部类对象
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        // 钩子方法,给子类用来替换装饰task,其实还是sft
        // 就是子类转父类而已。。
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        // 延迟执行任务
        delayedExecute(t);
        return t;
    }
    
  • delayedExecute

    private void delayedExecute(RunnableScheduledFuture<?> task) {
        // 判断任务是否已关闭
        if (isShutdown())
            reject(task);
        else {
            // 入队,这个就是ThreadPoolExecutor#wrokQueue任务队列
            // add 失败会抛出异常,队列满
            super.getQueue().add(task);
            // 再次检查线程池
            if (isShutdown() &&
                !canRunInCurrentRunState(task.isPeriodic()) &&
                remove(task))
                task.cancel(false);
            else
                // 保证空间足够让线程执行任务
                ensurePrestart();
        }
    }
    
    void ensurePrestart() {
        int wc = workerCountOf(ctl.get());
        if (wc < corePoolSize)
            // 创建工作线程(也就是等待队列)
            // 注意,这里没有传入firstTask采纳数,因为上面已经把线程扔入了队列
            // 同时,没有用maxPoolSize参数,所以最大线程参数在队列中是没有使用的
            addWorker(null, true);
        else if (wc == 0)
            addWorker(null, false);
    }
    
使用Demo
public class ThreadPoolTest03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建一个定时线程池
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(5);

        System.out.println("start: " + System.currentTimeMillis());

        // 执行一个无返回值任务,5秒后执行,只执行一次
        scheduledThreadPoolExecutor.schedule(() -> {
            System.out.println("spring: " + System.currentTimeMillis());
        }, 5, TimeUnit.SECONDS);

        // 执行一个有返回值任务,5秒后执行,只执行一次
        ScheduledFuture<String> future = scheduledThreadPoolExecutor.schedule(() -> {
            System.out.println("inner summer: " + System.currentTimeMillis());
            return "outer summer: ";
        }, 5, TimeUnit.SECONDS);
        // 获取返回值
        System.out.println(future.get() + System.currentTimeMillis());

        // 按固定频率执行一个任务,每2秒执行一次,1秒后执行
        // 任务开始时的2秒后
        scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
            System.out.println("autumn: " + System.currentTimeMillis());
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
        }, 1, 2, TimeUnit.SECONDS);

        // 按固定延时执行一个任务,每延时2秒执行一次,1秒执行
        // 任务结束时的2秒后,本文由公从号“彤哥读源码”原创
        scheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
            System.out.println("winter: " + System.currentTimeMillis());
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
        }, 1, 2, TimeUnit.SECONDS);
    }
}

五、总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值