• ScheduledExecutorService详解


    ScheduledExecutorService有线程池的特性,也可以实现任务循环执行,可以看作是一个简单地定时任务组件,因为有线程池特性,所以任务之间可以多线程并发执行,互不影响,当任务来的时候,才会真正创建线程去执行
    我们在做一些普通定时循环任务时可以用它,比如定时刷新字典常量,只需要不断重复执行即可,这篇文章讲解一下它的用法以及注意事项,不涉及底层原理

    注意:我们都知道,在使用线程池的时候,如果我们的任务出现异常没有捕获,那么线程会销毁被回收,不会影响其他任务继续提交并执行,但是在这里,如果你的任务出现异常没有捕获,会导致后续的任务不再执行,所以一定要try...catch

    1. 延迟不循环任务schedule方法

    schedule(Runnable command, long delay, TimeUnit unit)
    参数1:任务
    参数2:方法第一次执行的延迟时间
    参数3:延迟单位
    说明:延迟任务,只执行一次(不会再次执行),参数2为延迟时间

    案例说明:

    @Component
    @Slf4j
    public class MineExecutors {
        private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
        private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:sss");
    
        @PostConstruct
        public void init() {
            scheduler.schedule(() -> {
                try {
                    log.info("开始执行...time {}", format.format(new Date()));
                    Thread.sleep(1000);
                    log.info("执行结束...time {}", format.format(new Date()));
                } catch (Exception e) {
                    log.error("定时任务执行出错");
                }
            }, 5, TimeUnit.SECONDS);
    		log.info("初始化成功 {}", format.format(new Date()));
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    可以看到任务执行时间为初始化完成后5s才开始执行,且只执行一次
    在这里插入图片描述

    2. 延迟且循环cheduleAtFixedRate方法

    cheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
    参数1:任务
    参数2:初始化完成后延迟多长时间执行第一次任务
    参数3:任务时间间隔
    参数4:单位
    方法解释:是以上一个任务开始的时间计时,比如period为5,那5秒后,检测上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行,如果你的任务执行时间超过5秒,那么任务时间间隔参数将无效,任务会不停地循环执行,由此可得出该方法不能严格保证任务按一定时间间隔执行

    错误:任务连续执行案例:

    @Component
    @Slf4j
    public class MineExecutors {
    
        private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
    
        private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        @PostConstruct
        public void init() {
            scheduler.scheduleAtFixedRate(() -> {
                try {
                    log.info("开始执行...time {}", format.format(new Date()));
                    Thread.sleep(3000);
                    log.info("执行结束...time {}", format.format(new Date()));
                } catch (Exception e) {
                    log.error("定时任务执行出错");
                }
            }, 0, 2, TimeUnit.SECONDS);
            log.info("初始化成功 {}", format.format(new Date()));
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    由上面代码可以看出,任务执行需要3秒,而我们设定的任务时间间隔为2秒,如此就会导致任务连续执行,该方法不能严格保证任务按照规定的时间间隔执行,如果你的任务执行时间可以保证忽略不计,则可以使用该方法,我们可以看到下面日志,上一个任务的执行结束时间与下一个任务的开始时间一致,所以任务连续循环执行了
    在这里插入图片描述
    正确案例:

    @Component
    @Slf4j
    public class MineExecutors {
    
        private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
    
        private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        @PostConstruct
        public void init() {
            scheduler.scheduleAtFixedRate(() -> {
                try {
                    log.info("开始执行...time {}", format.format(new Date()));
                    Thread.sleep(1000);
                    log.info("执行结束...time {}", format.format(new Date()));
                } catch (Exception e) {
                    log.error("定时任务执行出错");
                }
            }, 0, 3, TimeUnit.SECONDS);
            log.info("初始化成功 {}", format.format(new Date()));
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    可以看到任务以上一次任务的开始时间,按3秒一次的方式执行
    在这里插入图片描述

    3. 严格按照一定时间间隔执行``

    scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
    参数1:任务
    参数2:初始化完成后延迟多长时间执行第一次任务
    参数3:任务执行时间间隔
    参数4:单位
    解释:以上一次任务执行结束时间为准,加上任务时间间隔作为下一次任务开始时间,由此可以得出,任务可以严格按照时间间隔执行

    案例:

    @Component
    @Slf4j
    public class MineExecutors {
    
        private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
    
        private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        @PostConstruct
        public void init() {
            scheduler.scheduleWithFixedDelay(() -> {
                try {
                    log.info("开始执行...time {}", format.format(new Date()));
                    Thread.sleep(5000);
                    log.info("执行结束...time {}", format.format(new Date()));
                } catch (Exception e) {
                    log.error("定时任务执行出错");
                }
            }, 0, 3, TimeUnit.SECONDS);
            log.info("初始化成功 {}", format.format(new Date()));
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    由下图日志可以看出,下次任务的开始时间是在上一次任务结束时间+任务时间间隔为准的,严格按照任务时间间隔,规律执行,如果你的任务需要保证严格的时间间隔,可以用该方法启动任务
    在这里插入图片描述

    其他用法与线程池没有差异了,例如ThreadFactory作为参数传入,自定义线程池内线程名称之类的,不多解释了。

    能力有限,不足之处,欢迎指正~

  • 相关阅读:
    宝塔面板搭建thinkphp(fastadmin)项目注意事项
    [ 云计算 | AWS ] IAM 详解以及如何在 AWS 中直接创建 IAM 账号
    1519_AURIX TC275 SRI总线部分相关寄存器的梳理
    DDoS和CC攻击的原理
    win11-qt5.14配置
    从数据库发展史看数据库未来技术趋势
    解决nacos集群搭建,服务注册失败
    详解Nacos和Eureka的区别
    JS-内置对象API-Array(数组)-(二)不改变原数组的API-篇
    大一新生HTML期末作业,网页制作作业——明星介绍易烊千玺网站HTML+CSS
  • 原文地址:https://blog.csdn.net/qq_41563912/article/details/126347720