原文作者:小付 

原文地址:ScheduledThreadPoolExecutor原理分析

目录

一、简单使用

二、类UML图

三、处理流程

四、任务提交方式

五、SchduledFutureTask之run方法实现


一、简单使用

这里先学会简单使用再深入探讨。

        ScheduledThreadPoolExecutor  scheduled = new ScheduledThreadPoolExecutor(2);scheduled.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {loge("time:");}}, 0, 40, TimeUnit.MILLISECONDS);//0表示首次执行任务的延迟时间,40表示每次执行任务的间隔时间,TimeUnit.MILLISECONDS执行的时间间隔数值单位
  • 间隔单位毫秒:TimeUnit.MILLISECONDS
  • 间隔单位秒:TimeUnit.SECONDS
  • 间隔单位分钟:TimeUnit.MINUTES
  • 间隔单位小时:TimeUnit.HOURS
  • 间隔单位天:TimeUnit.DAYS

二、类UML图

定时线程池类ScheduledThreadPoolExecutor用来处理延时任务或定时任务,它的类结构图如下:

三、处理流程

  1. 它的处理流程如下:主线程提交的定时任务或延时任务采用DelayQueue存储。
  2. DelayQueue内部封装了一个PriorityQueue(优先队列,是一种堆结构),它会根据time的先后时间排序,若 time相同则根据sequenceNumber排序;DelayQueue也是一个无界队列;
  3. 工作线程会从DelayQueue取已经到期的任务去执行;
  4. 执行结束后重新设置任务的到期时间,再次放回DelayQueue

PriorityQueue堆结构如下图:

可见,DelayedQueue是一个基于最小堆结构的队列。堆结构可以使用数 组表示,可以转换成如下的数组:

为什么要使用DelayedWorkQueue呢?定时任务执行时需要取出最近要执行的任务,所以任务在队列中每次出队时一定要是当 前队列中执行时间最靠前的,所以自然要使用优先级队列。DelayedQueue是一个优先级队列,它可以保证每次出队的任务都是当前队列中 执行时间最靠前的,由于它是基于堆结构的队列,堆结构在执行插入和删除操作时的最坏时 间复杂度是 O(logN)。PriorityQueue内部的比较逻辑实现代码如下:

    public int compareTo(Delayed other) {if (other == this) // compare zero if same objectreturn 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;elsereturn 1;}long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;}

四、任务提交方式

它接收SchduledFutureTask类型的任务,是线程池调度任务的最小单位,有三种提交任务的方式:

1、schedule()方法

首先是schedule方法,该方法是指任务在指定延迟时间到达后触发,只会执行一次。

public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit) {//参数校验if (command == null || unit == null)throw new NullPointerException();//这里是一个嵌套结构,首先把用户提交的任务包装成ScheduledFutureTask 8 //然后在调用decorateTask进行包装,该方法是留给用户去扩展的,默认是个 空方法RunnableScheduledFuture<?> t = decorateTask(command,new ScheduledFutureTask<Void>(command, null,triggerTime(delay, unit)));//包装好任务以后,就进行提交了                                  delayedExecute(t);return t;}

delayedExecute()方法是留给用户去扩展的,默认是个 空方法源码如下:

private void delayedExecute(RunnableScheduledFuture<?> task) {//如果线程池已经关闭,则使用拒绝策略把提交任务拒绝掉if (isShutdown())reject(task);else {//与ThreadPoolExecutor不同,这里直接把任务加入延迟队列super.getQueue().add(task);//如果当前状态无法执行任务,则取消if (isShutdown() &&!canRunInCurrentRunState(task.isPeriodic()) &&remove(task))task.cancel(false);else//这里是增加一个worker线程,避免提交的任务没有worker去执行//原因就是该类没有像ThreadPoolExecutor一样,woker满了才放入队列ensurePrestart();}}

2、scheduleAtFixedRate()

schedule和scheduleAtFixedRate的区别在于,如果指定开始执行的时间在当前系统运行时间之前,scheduleAtFixedRate会把已经过去的时间也作为周期执行,而schedule不会把过去的时间算上。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<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(period));RunnableScheduledFuture<Void> t = decorateTask(command, sft);sft.outerTask = t;delayedExecute(t);return t;}

SchduledFutureTask:从上面提交任务方法的源码的第六行ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit), unit.toNanos(period));可以看到scheduleAtFixedRate()方法将command封装为ScheduledFutureTask。下面再来看一下SchduledFutureTask接收的参数(成员变量):

 - private long time:任务开始的时间 - private final long sequenceNumber;:任务的序号 - private final long period:任务执行的时间间隔

五、SchduledFutureTask之run方法实现

run方法是调度task的核心,task的执行实际上是run方法的执行。

    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);}}}
  1. 如果当前线程池运行状态不可以执行任务,取消该任务,然后直接返回,否则执行 步骤2;
  2. 如果不是周期性任务,调用FutureTask中的run方法执行,会设置执行结果,然后 直接返回,否则执行步骤3;
  3. 如果是周期性任务,调用FutureTask中的runAndReset方法执行,不会设置执行 结果,然后直接返回,否则执行步骤4和步骤5;
  4. 计算下次执行该任务的具体时间;
  5. 重复执行任务。

reExecutePeriodic方法

void reExecutePeriodic(RunnableScheduledFuture<?> task) {if (canRunInCurrentRunState(true)) {super.getQueue().add(task);if (!canRunInCurrentRunState(true) && remove(task))task.cancel(false);elseensurePrestart();}}

该方法和delayedExecute方法类似,不同的是:

  1. 由于调用reExecutePeriodic方法时已经执行过一次周期性任务了,所以不会 reject当前任务;
  2. 传入的任务一定是周期性任务。

一切伟大的行动和思想,都有一个微不足道的开始。

Java并发编程—ScheduledThreadPoolExecutor原理分析相关推荐

  1. Java并发编程—AQS原理分析

    目录 一.AQS原理简述 二.自定义独占锁及共享锁 三.锁的可重入性 四.锁的公平性 五.惊群效应 AQS全称AbstractQueuedSynchronizer,它是实现 JCU包中几乎所有的锁.多 ...

  2. Java并发编程学习 + 原理分析(建议收藏)

    总结不易,如果对你有帮助,请点赞关注支持一下 微信搜索程序dunk,关注公众号,获取博客源码 Doug Lea是一个无私的人,他深知分享知识和分享苹果是不一样的,苹果会越分越少,而自己的知识并不会因为 ...

  3. Java 并发编程-不懂原理多吃亏(送书福利)

    作者 | 加多 关注阿里巴巴云原生公众号,后台回复关键字"并发",即可参与送书抽奖! ** 导读:并发编程与 Java 中其他知识点相比较而言学习门槛较高,从而导致很多人望而却步. ...

  4. Java并发编程—常见面试题

    建议: 学习java并发前需要先掌握JVM知识 关于下面问题档案的详细解析都在后面推荐的相关系列文章中 一.线程安全相关 1.什么叫线程安全? 线程安全就是说多线程访问同一代码,不会产生不确定的结果. ...

  5. Java 并发编程—有锁互斥机制及AQS理论

    原文作者:Java并发编程 原文地址:AQS这样学就很简单了 目录 一.有锁互斥机制 二.AQS如何实现互斥 三.结语 如果你是道格李,你要实现一套机制来保证线程互斥,你会如何实现呢?你肯定不会一上来 ...

  6. Java并发编程—定时器Timer底层原理

    原文作者:妮蔻 原文地址:Java并发编程笔记之Timer源码分析 目录 一.timer问题复现 二.Timer 实现原理分析 timer在JDK里面,是很早的一个API了.具有延时的,并具有周期性的 ...

  7. Java 并发编程——Executor框架和线程池原理

    Java 并发编程系列文章 Java 并发基础--线程安全性 Java 并发编程--Callable+Future+FutureTask java 并发编程--Thread 源码重新学习 java并发 ...

  8. Java并发编程包中atomic的实现原理

    转载自   Java并发编程包中atomic的实现原理 这是一篇来自粉丝的投稿,作者[林湾村龙猫]最近在阅读Java源码,这一篇是他关于并发包中atomic类的源码阅读的总结.Hollis做了一点点修 ...

  9. Java 并发编程CAS、volatile、synchronized原理详解

    CAS(CompareAndSwap) 什么是CAS? 在Java中调用的是Unsafe的如下方法来CAS修改对象int属性的值(借助C来调用CPU底层指令实现的): /*** * @param o ...

最新文章

  1. koa cookie使用
  2. plsql最多可以存多少_银行内部透露:如果有10万块钱,可以都放在余额宝里吗?存银行是不是更好?...
  3. 软件开发工程师证书有用吗_bim工程师证书有用吗
  4. C#中 标识符“XXX”不符合 CLS
  5. FR cpt报表的自动滚屏/滚动编辑
  6. python邮件发送 STMP
  7. js常用插件(八)之移动端滑动插件swiper,BScroll
  8. IT转型之路(一) 迷茫、困惑
  9. 高性能计算机重要的配件,电脑配件大盘点,为高性能保驾护航!
  10. 人人商城之导出excel表
  11. 第十三篇 Python建模库介绍
  12. 从事互联网产品运营所需的8条技能
  13. 加速度传感器灵敏度表示的几种方式LSB/g,count/g,V/g,V/°,请给出几种方式之间的说明及转换公式
  14. discuz!x 应用中心更新为新地址解决方案之一
  15. Carla release与ros-bridge
  16. 安装superset(详细版)
  17. 跟着尚硅谷学大数据(二)MapReduce
  18. 计算机vfp的解释,整理的一些VFP基本概念
  19. NOIP模拟试题详讲2021/11/9
  20. 用C#实现键盘鼠标动作捕获

热门文章

  1. 物理磁盘空间使用已满导致数据库hang起
  2. 也谈ASP.NET页面事件
  3. ObjectArx开发对txt文本文件的操作一例
  4. [导入]博客搬家了,呵呵
  5. java 关键字volatile的作用
  6. Sql 08数据库还原数据库时一直提示数据库被占用
  7. 性能测试培训:定位jvm耗时函数
  8. Cannot SET AUTOTRACE 处理办法
  9. 最新卡通渲染效果图(附带一张次世代帅哥)
  10. 锁定弹出层(jquery语法)