JUC - 14 线程池系列之ScheduledThreadPoolExecutor (一)_scheduledthreadpoolexecutor 支持lamda么-程序员宅基地

技术标签: JUC  

ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类,同时实现了ScheduledExecutorService接口。

public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService

ScheduledThreadPoolExecutor的功能主要有两点:在固定的时间点执行(也可以认为是延迟执行),重复执行。

和分析ThreadPoolExecutor时一样,首先来看核心方法execute:

 public void execute(Runnable command) {
        schedule(command, 0, TimeUnit.NANOSECONDS);
    }

execute方法调用了另外一个方法schedule,同时我们发现三个submit方法也是同样调用了schedule方法,因为有两种类型的任务:Callable和Runnable,因此schedule也有两个重载方法。

public ScheduledFuture<?> schedule(Runnable command,
                                       long delay,
                                       TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        RunnableScheduledFuture<?> t = decorateTask(command,
            new ScheduledFutureTask<Void>(command, null,
                                          triggerTime(delay, unit)));
        delayedExecute(t);
        return t;
    }

    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay,
                                           TimeUnit unit) {
        if (callable == null || unit == null)
            throw new NullPointerException();
        RunnableScheduledFuture<V> t = decorateTask(callable,
            new ScheduledFutureTask<V>(callable,
                                       triggerTime(delay, unit)));
        delayedExecute(t);
        return t;
    }

两个方法逻辑基本一致,都是把任务包装成RunnableScheduledFuture对象,然后调用delayedExecute来实现延迟执行。任务包装类继承自ThreadPoolExecutor的包装类RunnableFuture,同时实现ScheduledFuture接口使包装类具有了延迟执行和重复执行这些功能以匹配ScheduledThreadPoolExecutor。

因此首先来看ScheduledFutureTask,以下是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;

来看核心方法run:

public void run() {
            boolean periodic = isPeriodic();
            // 检测是否可以运行任务,这里涉及到另外两个变量:continueExistingPeriodicTasksAfterShutdown
            // 和executeExistingDelayedTasksAfterShutdown
            // 前者允许在shutdown之后继续执行重复执行的任务
            // 后者允许在shutdown之后继续执行延时执行的任务,
            // 因此这里根据任务是否为periodic来决定采用哪个选项,然后
            // 如果线程池正在运行,那么肯定可以执行
            // 如果正在shutdown,那么要看选项的值是否为true来决定是否允许执行任务
            // 如果不被允许的话,就会取消任务
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            // 如果可以执行任务,对于不用重复执行的任务,直接执行即可
            else if (!periodic)
                ScheduledFutureTask.super.run();
            // 对于需要重复执行的任务,则执行一次,然后reset
            // 更新一下下次执行的时间,调用reExecutePeriodic更新任务在执行队列的
            // 位置(其实就是添加到队列的末尾)
            else if (ScheduledFutureTask.super.runAndReset()) {
                setNextRunTime();
                reExecutePeriodic(outerTask);
            }
        }

因此这里可以得出关于重复执行的实现:任务执行一次,Reset状态,重新加入到任务队列。

回到delayedExecute,它可以保证任务在准确时间点执行,来看delayedExecute是如果实现延迟执行的:

private void delayedExecute(RunnableScheduledFuture<?> task) {
        if (isShutdown())
            reject(task);
        else {
            super.getQueue().add(task);
            if (isShutdown() &&
                !canRunInCurrentRunState(task.isPeriodic()) &&
                remove(task))
                task.cancel(false);
            else
                ensurePrestart();
        }
    }

乍看之下,发现也就是把任务加入到任务队列中,那么这个延时执行的功能是如何实现的,秘密就在任务队列的实现。

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue());
    }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

ScheduledThreadPoolExecutor的任务队列不是普通的BlockingQueue,而是一个特殊的实现DelayedWorkQueue。下一篇文章就来说说这个DelayedWorkQueue。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wszhongguolujun/article/details/89709655

智能推荐

在SpringBoot中启动时关于连接数据库失败的问题_springboot启动时数据库连接失败 不关闭-程序员宅基地

文章浏览阅读2.2k次。#在SpringBoot中启动时关于连接数据库失败的问题对照了application.yml,发现配置文件貌似没什么问题,但是在查找信息之后,发现问题正是出现在application.yml中问题出于datasource下的data-username和data-password只要将data-username和data-password改为username和password即可..._springboot启动时数据库连接失败 不关闭

antd-pro(V5)动态菜单_antdpro的菜单-程序员宅基地

文章浏览阅读4.6k次。一般情况下登录系统后菜单是由后端返回的,不是前端写死的。antd-pro也支持,修改的路径在app.tsx在 layout 里加一个menuDataRender字段先给一个() =>[]可以看到左侧菜单没了,说明配置生效了,接下来就可以围绕这个配置做文章了,我们先定义一个 menuDataRender方法。根据登录缓存到本地的数据做下处理,判断菜单里要展示哪些内容(比如替换字段,隐藏不显示的菜单,隐藏按钮等),处理好了后返回一个数组结构即可。示例代码如下export const layout: _antdpro的菜单

Linux安装使用jprofiler6分析服务器应用状态-程序员宅基地

文章浏览阅读77次。为什么80%的码农都做不了架构师?>>> ..._jprofiler6 key

苏小红C语言第四版课后习题练习7.7最大公约数三种计算方式_c语言程序设计第四版课后题答案苏小红第七章-程序员宅基地

文章浏览阅读170次。(可以看出递归算法更加侧重于计算的技巧,并且计算机计算的次数也相对更少);_c语言程序设计第四版课后题答案苏小红第七章

[PyTorch小试牛刀]实战六·准备自己的数据集用于训练(基于猫狗大战数据集)_pytorch入门与实践 dogsvscats pycharm运行-程序员宅基地

文章浏览阅读3.8k次。[PyTorch小试牛刀]实战六·准备自己的数据集用于训练(基于猫狗大战数据集)在上面几个实战中,我们使用的是Pytorch官方准备好的FashionMNIST数据集进行的训练与测试。本篇博文介绍我们如何自己去准备数据集,以应对更多的场景。我们此次使用的是猫狗大战数据集,开始之前我们要先把数据处理一下,形式如下datas│└───train│ ││ └───cats│ ..._pytorch入门与实践 dogsvscats pycharm运行

CS61C Lab 攻略:从入门到升天-程序员宅基地

文章浏览阅读968次,点赞3次,收藏4次。CS61C 主要内容为计算机组成原理,重难点是实验(Lab)和项目(Project),当然课程的精华也是实验和项目。本文是对 CS61C 的实验进行分析思考和总结,为项目做好铺垫。_cs61c

随便推点

环境变量的加载顺序、环境变量集合_环境变量的顺序-程序员宅基地

文章浏览阅读1k次。*******字符编码ASCII,GB2312,GBK,Unicode,UTF-8比较参考:https://blog.csdn.net/softwarenb/article/details/51994943**环境变量的加载顺序:Mac系统的环境变量,加载顺序为:a. /etc/profileb. /etc/pathsc. ~/.bash_profiled. ~/..._环境变量的顺序

科学家发现让人类幸福感飙升的密码!给大脑植入这个算法 | 精选-程序员宅基地

文章浏览阅读316次。▼大型年度AI人物评选——2017中国AI英雄风云榜已于12月4日在乌镇张榜,12月18日在北京国贸三期举行颁奖典礼。榜单评选出年度技术创新人物TOP 10;商业创新人物TOP 10,获取完整榜单请关注网易智能公众号(ID:smartman163),回复关键词“评奖”。本文系网易智能工作室出品聚焦AI,读懂下一个大时代【网易智能讯12月10日消息】不只有你会_人类大脑植入代码

正则表达式匹配中括号内的内容_正则<>里内容-程序员宅基地

文章浏览阅读3.6k次。几经研究, 终于实现了。time[2020-06-04 11:43:36](?<=\[)(.*)(?=])(pattern) 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。 (?:pattern) 匹配 pattern 但不获取匹配结..._正则<>里内容

C++程序启动时报“R6030 CRT not initialized”错误_r6030 -crt not initialized-程序员宅基地

文章浏览阅读1.4w次,点赞11次,收藏12次。SPY++工具注入到C++程序的进程中,导致程序启动时报“R6030 CRT not initialized”错误,本文将讲解该问题的排查过程。_r6030 -crt not initialized

实现文字平滑弯曲弧形效果的插件-arctext.js_vue-arc-text-程序员宅基地

文章浏览阅读5.5k次,点赞4次,收藏11次。扇形的文字有时候产品大佬就是很任性,说做一个宣传页,一个类似拱门的效果,每次文字不一样,但是文字得呈现拱形状,类似上图啦。尝试自己使用canvas画和css3的rotate旋转div,两种方法都是计算旋转角度的时候很麻烦,因为可能5个字10个字,但是得均匀地呈拱形分布,要知道让每个文字都沿着弯曲路径排布相当的复杂,于是便发现了这个好用的插件..._vue-arc-text

OC:跟随小码哥一起学习KVC,42岁程序员面试-程序员宅基地

文章浏览阅读851次,点赞26次,收藏13次。今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。还有。