调度线程池:

  1. 调度线程池是线程池类ThreadPoolExecutor的子类,复用了线程池的计算框架,主要用于解决任务在一定的时间间隔后重复执行的问题。

  2. 例子

    public class ScheduledThreadPoolTest {/*** 以固定的频率调度,包括任务执行的时间*/public static void scheduledAtFixRate(){ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);//任务开始执行的延迟时间int initDelay = 1;//任务循环执行的间隔int period = 5;scheduledExecutorService.scheduleAtFixedRate(new Task(),initDelay,period,TimeUnit.SECONDS);}/*** 以固定的时间间隔调度,不包括任务执行的时间*/public static void scheduledWithFixDelay(){ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);//任务开始执行的延迟时间int initDelay = 1;//任务循环执行的间隔int period = 5;scheduledExecutorService.scheduleWithFixedDelay(new Task(),initDelay,period,TimeUnit.SECONDS);}static class Task implements Runnable{@Overridepublic void run() {System.out.println(String.format("开始执行任务调度,执行线程:%s",Thread.currentThread().getName()));try {//模拟任务的执行时间为5秒Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行任务调度完成");}}public static void main(String[] args){ScheduledThreadPoolTest.scheduledAtFixRate();ScheduledThreadPoolTest.scheduledWithFixDelay();}
    }
    
    1. 其中,ScheduledThreadPoolTest.scheduleAtFixRate()方法,会启动一个调度线程池,并以初始延迟1s,固定频率为5s,使用调度线程池的scheduleAtFixedRate执行任务Task,任务Task的执行时间为5s。
    2. ScheduledThreadPoolTest.scheduleWithFixDelay()方法,会启动一个调度线程池,并以初始延迟1s,固定频率为5s,使用调度线程池的scheduleWithFixedDelay执行任务Task,任务Task的执行时间为5s。
    3. 那么,这两个方法的区别在哪里呢?scheduleAtFixRate是以固定的频率执行任务,在本例中任务开始执行的时间点(以当前时间为起点)为:1s,6s,11s,16s,…,该方法和任务本身的执行时间无关,以固定的频率触发任务执行。而scheduleWithFixDelay是以固定的延迟执行任务,在本例中任务开始执行的时间点(以当前时间为起点)为:1s,11s,21s,31s,…,该方法和任务本身的执行时间相关,在任务执行完成后间隔5s再开始执行下一次任务。
  3. ScheduledExecutorService:

    //Executors类的newScheduledThreadPool方法
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);}
    
    //可以看到该类是ThreadPoolExecutor的子类,并且构造函数里面向父类传递了一个类型为DelayedWorkQueue的阻塞队列.
    public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}...
    }
    
  4. 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;}private void delayedExecute(RunnableScheduledFuture<?> task) {if (isShutdown())reject(task);else {//将任务添加进阻塞队列,这个阻塞队列就是上面提的DelayedWorkQueuesuper.getQueue().add(task);if (isShutdown() &&!canRunInCurrentRunState(task.isPeriodic()) &&remove(task))task.cancel(false);elseensurePrestart();}
    }
    
  5. ScheduledFutureTask是如何实现任务循环执行:

    private class ScheduledFutureTask<V>extends FutureTask<V> implements RunnableScheduledFuture<V> {/*** 任务执行的具体方法,还记得ThreadPoolExecutor的runWorker方法吗?方法内部会调用任务的run()方法以执行具体的任务*/public void run() {//判断任务是否需要周期执行boolean periodic = isPeriodic();if (!canRunInCurrentRunState(periodic))cancel(false);else if (!periodic)ScheduledFutureTask.super.run();//执行具体的任务,调用最原始任务的run()方法    else if (ScheduledFutureTask.super.runAndReset()) {//计算任务下一次开始执行的时间setNextRunTime();//重新将任务放入阻塞队列,DelayedWorkQueuereExecutePeriodic(outerTask);}} //计算任务下一次执行时间private void setNextRunTime() {long p = period;if (p > 0)time += p;elsetime = triggerTime(-p);} void reExecutePeriodic(RunnableScheduledFuture<?> task) {if (canRunInCurrentRunState(true)) {//将任务放入阻塞队列,DelayedWorkQueuesuper.getQueue().add(task);if (!canRunInCurrentRunState(true) && remove(task))task.cancel(false);elseensurePrestart();}}
    }
    

    可以看到这里的关键是在执行任务的同时,计算任务的下一次执行时间,并用原始任务生成一个新的任务重新放入阻塞队列等待执行。

  6. 阻塞队列DelayedWorkQueue:

    static class DelayedWorkQueue extends AbstractQueue<Runnable>implements BlockingQueue<Runnable>//线程池的Worker会从阻塞队列中取任务,调用的就是队列的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 {//计算任务是否到达执行时间,<0代表到达执行时间直接返回,否则等待long delay = first.getDelay(NANOSECONDS);if (delay <= 0)//从队列中出队,并维护堆(此处队列内部是用堆来实现的)return finishPoll(first);first = null; // don't retain ref while waiting//leader不为空,说明其他线程正在获取任务,当前线程必须等待if (leader != null)available.await();else {//leander指的是处理队列中第一个任务的线程,leader为空,将当前线程设置为leaderThread thisThread = Thread.currentThread();leader = thisThread;try {// 阻塞一定时间(调度任务的时间)available.awaitNanos(delay);} finally {if (leader == thisThread)leader = null;}}}}} finally {if (leader == null && queue[0] != null)//任务获取成功后,唤醒其他等待的线程available.signal();lock.unlock();}}
    }
    

Java并发编程之调度线程池相关推荐

  1. Java 并发编程之自定义线程池 ThreadPoolExecutor

    1)定义一个任务线程 public class Task implements Runnable {private String name;Task(String name) {this.name = ...

  2. 【Java进阶】Java并发类库提供的线程池有哪几种? 分别有什么特点?

    我在专栏第 17 讲中介绍过线程是不能够重复启动的,创建或销毁线程存在一定的开销,所以利用线程池技术来提高系统资源利用效率,并简化线程管理,已经是非常成熟的选择. 今天我要问你的问题是,Java 并发 ...

  3. java并发编程实践(2)线程安全性

    [0]README 0.0)本文部分文字描述转自:"java并发编程实战", 旨在学习"java并发编程实践(2)线程安全性" 的相关知识: 0.1)几个术语( ...

  4. Java并发(五)线程池使用番外-分析RejectedExecutionException异常

    Java并发(五)线程池使用番外-分析RejectedExecutionException异常 参考文章: (1)Java并发(五)线程池使用番外-分析RejectedExecutionExcepti ...

  5. Java并发编程—什么是线程?

    原文作者:way_more 原文地址:Java 多线程常见基础面试题总结,面试必看! 目录 一.什么是线程和进程? 二.简要描述线程与进程的关系 三.FAQ 一.什么是线程和进程? 1.1. 何为进程 ...

  6. java并发编程第一课 线程的创建、停止和状态变更

    开篇词: 由点及面,搭建你的 Java 并发知识网 你好,欢迎学习<Java 并发编程核心 78 讲>,我是讲师星星,一线互联网公司资深研发工程师,参与过集团内多个重点项目的设计与开发. ...

  7. 【JUC并发编程11】线程池

    文章目录 线程池 11.1 线程池概述 11.2 线程池架构 11.3 线程池使用方式 11.4 线程池底层原则 11.5 线程池的七个参数 11.6 线程池底层工作流程 11.7 自定义线程池 线程 ...

  8. Java并发编程(02):线程核心机制,基础概念扩展

    本文源码:GitHub·点这里 || GitEE·点这里 一.线程基本机制 1.概念描述 并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效 ...

  9. Java并发编程(01):线程的创建方式,状态周期管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.并发编程简介 1.基础概念 程序 与计算机系统操作有关的计算机程序.规程.规则,以及可能有的文件.文档及数据. 进程 进程是计算机中的程序 ...

最新文章

  1. PHP学习笔记 第八讲 Mysql.简介和创建新的数据库
  2. php fastcgi_finish_request用法
  3. tcp/ip 协议栈Linux内核源码分析13 udp套接字发送流程二
  4. C++内联(inline)函数
  5. UTF-8中的BOM-带还是不带?
  6. HUE Schedule 定时调度 - 启动时间设置问题(执行次数过多,时区问题)
  7. Docker入门系列(一):目标和安排
  8. API 日调用量超 100 亿次!腾讯云首次披露云原生产品数据
  9. mysql数据表设计 对应成二维数组_结果集数据怎么转换成二维数组
  10. myql GROU_CONCAT 与FIND_IN_SET查询结果为空问题解决
  11. Postman使用详解
  12. Unity游戏开发 头发飘动效果
  13. 修改用友服务器ip地址,修改用友服务器ip地址
  14. Opencv中的数据存储(1)
  15. 关于photoshop软件的177个小技巧
  16. SPSS教程:数据不符合正态分布,如何统计描述
  17. 太空工程师-脚本-飞船姿态矫正
  18. 光伏电站清扫机器人_光伏清扫机器人(GF01B)
  19. 胜为蓝牙适配器驱动_胜为UCD-324蓝牙适配器驱动
  20. CCV (ICF DPM)

热门文章

  1. 【智能工厂】智能工厂如何快速落地
  2. chrome浏览器导出书签 如何导入收藏夹
  3. 将python源文件打包成exe文件
  4. 培养创造力的10个注意点
  5. shell export 作用
  6. python规则引擎开发经验_关于物联网规则引擎技术,你想要知道的都在这儿!
  7. 比亚迪汉家族3月热销12359辆 汉EV单车销量破万
  8. 苹果今年秋季或发布史上最多新品
  9. 华为P50 Pro外观基本确认:居中开孔全面屏,首发鸿蒙操作系统
  10. 报告称相比南方 数字化平台对北方小微商家助力作用更大