zoukankan      html  css  js  c++  java
  • 线程池之ScheduledThreadPoolExecutor概述

    简介

    在探讨时 ThreadPoolExecutor 只介绍了FixedThreadPool、CachedThreadPool、SingleThreadExecutor,并没有去介绍ScheduledThreadPoolExecutor,因为 ScheduledThreadPoolExecutor 与其他线程池的概念有些区别,它是一个支持任务周期性调度的线程池。

    ScheduledThreadPoolExecutor 继承 ThreadPoolExecutor,同时通过实现 ScheduledExecutorSerivce 来扩展基础线程池的功能,使其拥有了调度能力。其整个调度的核心在于内部类 DelayedWorkQueue ,一个有序的延时队列。

     
    ScheduledThreadPoolExecutor类图.png

    ScheduledThreadPoolExecutor 的出现,很好的弥补了传统 Timer 的不足,具体对比看下表:

     TimerScheduledThreadPoolExecutor
    线程 单线程 多线程
    多任务 任务之间相互影响 任务之间不影响
    调度时间 绝对时间 相对时间
    异常 单任务异常,
    后续任务受影响
    无影响

    构造方法

    ScheduledThreadPoolExecutor有三个构造形式:

    public ScheduledThreadPoolExecutor(int corePoolSize,
                                        ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory);
    }
    
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), handler);
    }
    
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory,
                                       RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler);
    }
    

    关于父类的构造可参见 ThreadPoolExecutor。当然我们也可以使用工具类Executors的newScheduledThreadPool的方法,快速创建。注意这里使用的DelayedWorkQueue

    ScheduledThreadPoolExecutor没有提供带有最大线程数的构造函数的,默认是Integer.MAX_VALUE,说明其可以无限制的开启任意线程执行任务,在大量任务系统,应注意这一点,避免内存溢出。

    核心方法

    核心方法主要介绍ScheduledThreadPoolExecutor的调度方法,其他方法与 ThreadPoolExecutor 一致。调度方法均由 ScheduledExecutorService 接口定义:

    public interface ScheduledExecutorService extends ExecutorService {
        // 特定时间延时后执行一次Runnable
        public ScheduledFuture<?> schedule(Runnable command,
                                           long delay, TimeUnit unit);
        // 特定时间延时后执行一次Callable
        public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                               long delay, TimeUnit unit);
        // 固定周期执行任务(与任务执行时间无关,周期是固定的)
        public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                      long initialDelay,
                                                      long period,
                                                      TimeUnit unit);
         // 固定延时执行任务(与任务执行时间有关,延时从上一次任务完成后开始)
        public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                         long initialDelay,
                                                         long delay,
                                                         TimeUnit unit);
    }
    

    代码中注释了每个方法的作用,需注意固定周期与固定延时的区别。下面分别对这些方法进行测试:

    public class ScheduledPoolTest {
        
        private static final SimpleDateFormat FORMAT = new SimpleDateFormat("hh:mm:ss");
        
        private static final Random RANDOM = new Random();
        
        /**
         * 输出:
         *  11:04:32
            11:04:35
         */
        public static void schedule() {
            ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
            printTime();
            scheduledExecutorService.schedule(new Task(), 3, TimeUnit.SECONDS);
        }
        
        /**
         * 输出:
         *  11:05:34
            11:05:36
            11:05:46
            11:05:56
            11:06:06
            11:06:16
            ......
         */
        public static void scheduleAtFixedRate() {
            ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
            printTime();
            scheduledExecutorService.scheduleAtFixedRate(new Task(), 2, 10, TimeUnit.SECONDS);
        }
        
        /**
         * 输出:
         *  11:07:39
            11:07:41
            11:07:54
            11:08:08
            11:08:22
            11:08:33
            ......
         */
        public static void scheduleWithFixedDelay() {
            ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
            printTime();
            scheduledExecutorService.scheduleWithFixedDelay(new Task(), 2, 10, TimeUnit.SECONDS);
        }
        
        static class Task implements Runnable{
            public void run() {
                printTime();
                try {
                    Thread.sleep(RANDOM.nextInt(5) * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
        public static void printTime() {
            Date date = new Date();
            System.out.println(FORMAT.format(date));
        }
    }
    

    为了体现scheduleAtFixedRate和scheduleWithFixedDelay的差别,在代码中我们加入了随机睡眠时间,使任务执行不确定。从注释中的输出我们可以看到scheduleAtFixedRate的任务运行周期不受任务执行时间的影响,而scheduleWithFixedDelay的任务运行周期受任务执行时间影响较大。

    但需注意,如果任务的执行时间超过任务调度周期,比如任务执行需要10s,而给定执行时间间隔是5s的话,任务的调度是在任务10s执行完之后立即重新执行,而不是5s的周期。

    总结

    ScheduledThreadPoolExecutor 在 ThreadPoolExecutor 的基础上扩展了 线程周期调度功能,使用时应注意控制其调度的时间点。

    多线程系列目录(不断更新中):



    作者:徐志毅
    链接:https://www.jianshu.com/p/5d994ee6d4ff
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    9、Spring Boot 2.x 集成 Thymeleaf
    【专题】Spring Boot 2.x 面试题
    8、Spring Boot 2.x 服务器部署
    7、Spring Boot 2.x 集成 Redis
    6、Spring Boot 2.x 集成 MyBatis
    5、Spring Boot 2.x 启动原理解析
    4、Spring Boot 2.x 自动配置原理
    3、Spring Boot 2.x 核心技术
    2、Spring Boot 2.x 快速入门
    centOS下安装JDK1.8.60,glassfish4.1.1以及MySQL
  • 原文地址:https://www.cnblogs.com/javalinux/p/14262321.html
Copyright © 2011-2022 走看看