ScheduledThreadPoolExecutor继承与基础线程池类ThreadPoolExecutor并实现ScheduledExecutorService接口。

其中ScheduledExecutorService继承与ExecutorService接口并添加了scheduleAtFixedRate和scheduleWithFixedDelay等方法。

两个方法的区别是前者是周期性的按照一定的时间进行任务的执行。如果一个任务执行超过了周期时间,则任务执行完之后会马上进行下一次任务的执行。而后者在这样的情况出现的时候会在任务执行完之后仍然间隔周期时间进行下一次任务的执行。

ScheduledThreadPoolExecutor内部封装了一个ScheduledFutureTask<V>类。该类继承与FutureTask<V>实现RunnableScheduledFuture<V>

ScheduledFutureTask<V>内部有对应的任务的比较器。包含sequenceNumber记录任务进入队列的序号。内部的关键方法为runPeriodic()方法

private void runPeriodic() {

  • boolean ok = ScheduledFutureTask.super.runAndReset();
  • boolean down = isShutdown();
  • // Reschedule if not cancelled and not shutdown or policy allows
  • if (ok && (!down ||
  • (getContinueExistingPeriodicTasksAfterShutdownPolicy() &&
  • !isStopped()))) {
  • long p = period;
  • if (p > 0)
  • time += p;
  • else
  • time = now() - p;
  • ScheduledThreadPoolExecutor.super.getQueue().add(this);
  • }
  • // This might have been the final executed delayed
  • // task.  Wake up threads to check.
  • else if (down)
  • interruptIdleWorkers();
  • }

该方法处理周期性执行的和非周期性执行的任务。其中period传入的值有正有负。这也是scheduleAtFixedRate和scheduleWithFixedDelay对应的区别。最后将相应的任务加入到对应的线程池的阻塞队列中。如果线程池关闭了需要调用对应的线程池关闭的处理。

对应的run方法为:

public void run() {

  • if (isPeriodic())
  • runPeriodic();
  • else
  • ScheduledFutureTask.super.run();
  • }

private void delayedExecute(Runnable command) {
        if (isShutdown()) {
            reject(command);
            return;
        }
        if (getPoolSize() < getCorePoolSize())
            prestartCoreThread();

super.getQueue().add(command);
    }
首次将任务加入到队列中的方法。

public boolean prestartCoreThread() {
        return addIfUnderCorePoolSize(null);
    }

private boolean addIfUnderCorePoolSize(Runnable firstTask) {
        Thread t = null;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (poolSize < corePoolSize && runState == RUNNING)

t = addThread(firstTask);

} finally {

mainLock.unlock();

}

if (t == null)

return false;         t.start();         return true; }

如果当前线程数小于核心线程数。则会新创建一个线程。新创建线程的时候会传入首个任务。

ScheduledThreadPoolExecutor中的默认拒绝策略是AbortPolicy 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常 其它的拒绝策略为:

AbortPolicy         -- 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。
CallerRunsPolicy    -- 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
DiscardOldestPolicy -- 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
DiscardPolicy       -- 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

FutureTask<V>类的内部是通过Sync和Callable<V>来构建相应的任务Sync是对应的同步器继承于AQS(内部基于一个链表的形式来维护一个等待队列,用其内部的Node节点进行构建)。AQS内部的原理感兴趣的的可以看下,此外ReenTrantLock内部的实现则是基于sync和AQS。

此外ScheduledThreadPoolExecutor内部封装了一个DelayedWorkQueue。其内部应用DelayQueue实现。对DelayQueue感兴趣的可以看下。

ScheduledThreadPoolExecutor源码主要部分解析的更多相关文章

  1. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  2. InfluxDB源码目录结构解析

    操作系统 : CentOS7.3.1611_x64 go语言版本:1.8.3 linux/amd64 InfluxDB版本:1.1.0 influxdata主目录结构 [root@localhost ...

  3. Spring源码入门——DefaultBeanNameGenerator解析 转发 https://www.cnblogs.com/jason0529/p/5272265.html

    Spring源码入门——DefaultBeanNameGenerator解析   我们知道在spring中每个bean都要有一个id或者name标示每个唯一的bean,在xml中定义一个bean可以指 ...

  4. 笔记-twisted源码-import reactor解析

    笔记-twisted源码-import reactor解析 1.      twisted源码解析-1 twisted reactor实现原理: 第一步: from twisted.internet ...

  5. 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银 | 百篇博客分析OpenHarmony源码 | v53.02

    百篇博客系列篇.本篇为: v53.xx 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银 | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ...

  6. v72.01 鸿蒙内核源码分析(Shell解析) | 应用窥伺内核的窗口 | 百篇博客分析OpenHarmony源码

    子曰:"苟正其身矣,于从政乎何有?不能正其身,如正人何?" <论语>:子路篇 百篇博客系列篇.本篇为: v72.xx 鸿蒙内核源码分析(Shell解析篇) | 应用窥视 ...

  7. Java并发包源码学习系列:线程池ScheduledThreadPoolExecutor源码解析

    目录 ScheduledThreadPoolExecutor概述 类图结构 ScheduledExecutorService ScheduledFutureTask FutureTask schedu ...

  8. 从源码的角度解析View的事件分发

    有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...

  9. ashMap源码阅读与解析

    目录结构 导入语 HashMap构造方法 put()方法解析 addEntry()方法解析 get()方法解析 remove()解析 HashMap如何进行遍历 导入语 HashMap是我们最常见也是 ...

随机推荐

  1. 几款一元单片机对比:CMS8S5880、STM8S003、N76E003

    大概17年开始,STM8S003的价格被贸易商炒货,变得很不稳定,一度上涨到2~3元,因为市场需求大增,小家电.无线充和一些简单功能的产品,本人就有在空气净化器.433M触摸开关.数据收发模块.红外控 ...

  2. java中int和Integer的区别?为什么有了int还要有设计Integer?

    参考https://blog.csdn.net/chenliguan/article/details/53888018 https://blog.csdn.net/myme95/article/det ...

  3. Windows 程序设计(3) 关于字符串

    1. 宽窄字节的区别及重要性 1.1 宽窄字节简介: C语言/C++语言,使用的字符串指针就是 char* 类型,C++中的字符串是 string,内部也是对 char* 的封装 窄字节 其实最早的系 ...

  4. Python3-subprocess模块-子进程管理

    简单介绍 subprocess模块可以创建新的进程,执行shell命令.Python脚本等 代码示例 import subprocess # 1.执行进程,并获取返回码 return_code = s ...

  5. 实战:IDEA运行速度调优

    序言 可能大家觉得系统调优一般都是针对服务端应用而言的,普通Java开发人员很少有机会实践.今天就通用一个Java开发人员日常工作中经常使用的开发工具开做一次调优实战. 我在日常工作中的主要IDE工具 ...

  6. 【贪心】Emergency Evacuation

    题目 大致题意 把指定的人从同一出口送出车外,且同一位置不能同时有两个人,求所需的最短时间. 分析 第一感觉就是利用贪心思想解决问题,但是这道题的数据范围用模拟的话肯定是会爆掉的,所以这是不可取的.我 ...

  7. 手把手教你部署 Redis Cluster

    环境准备 阿里云申请3台ECS,如下: 序号 内网IP OS A 172.16.190.78 CentOS 7.6 B 172.16.242.36 CentOS 7.6 C 172.16.190.77 ...

  8. java重试

    项目中有很多需要重试的场景,而每次都得写如下的逻辑 for (int i=0;i++;i<retry){ try{ do(//逻辑代码); if(success){ break; } }catc ...

  9. 初探RabbitMQ消息队列

    SpringBoot 是为了简化 Spring 应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖 ...

  10. ORA-12154: TNS:could not resolve the connect identifier specified. Solved.

    进入系统属性页面 rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,, 并添加系统环境变量 ORACLE_HOME = ${path} TNS_ADM ...