行业资讯 2025年08月6日
0 收藏 0 点赞 499 浏览 2293 个字
摘要 :

文章目录 演示代码 说下原因解析 scheduleWithFixedDelay和scheduleWithFixedRate表现差异 总结 最近在开发项目遇到一个问题就是关ScheduledThreadPoolExecutor线程池……




  • 演示代码
  • 说下原因解析
  • scheduleWithFixedDelay和scheduleWithFixedRate表现差异
  • 总结

最近在开发项目遇到一个问题就是关ScheduledThreadPoolExecutor线程池在周期性执行任务代码时,当任务代码中存在阻塞情况,定时任务是否还会继续执行一次的问题。为此潘老师做了一个简单的演示验证,效果如下。

首选潘老师说下结论,就是如果ScheduledThreadPoolExecutor线程池的周期性执行任务代码中存在阻塞代码,无论是scheduleWithFixedDelay还是scheduleWithFixedRate定时任务在阻塞期间都不会继续执行下一次。但是两者在使用方面还是有点区别,后面会说明下,接下来潘老师以scheduleWithFixedDelay来说下演示结果和原因:

演示代码

这里我们使用核心线程数(corePoolSize)为1的周期性线程池进行演示,使用ArrayBlockingQueue模拟阻塞,代码如下:

public class Test {
    private static final ScheduledThreadPoolExecutor SCHEDULED_EXECUTOR = new ScheduledThreadPoolExecutor(1);
    public static void main(String[] args) {
        ArrayBlockingQueue queue = new ArrayBlockingQueue(16);
        SCHEDULED_EXECUTOR.scheduleWithFixedDelay(() -> {
            System.out.println("开始取队列数据...");
            try {
                queue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("成功取出数据...");
        }, 0, 3, TimeUnit.SECONDS);
    }
}

我们会发现当ArrayBlockingQueue对列没有数据可以获取实现阻塞时,定时任务就停止了:
ScheduledThreadPoolExecutor周期性任务代码阻塞定时任务还会继续执行下一次吗?

如果我们在ArrayBlockingQueue中预先放置两个数据,会定时获取出这两个数据,当获取第3个数据就会阻塞,定时任务也会终止,直到有数据获取到才会恢复定时任务。

另外还有一个误区就是如果我们把线程池核心数量调整为2甚至更多,当第一个线程的业务代码实现阻塞,很多同学会误以为当周期性时间到了会启动空闲的线程来继续该任务,也是错误的,每个线程之间是独立的,线程池也不会这样去分配周期性任务,只有当你再调用scheduleWithFixedDelay方法时(即提交新任务),才会继续分配线程池中的空闲线程,不要把这个搞混了。

说下原因解析

ScheduledThreadPoolExecutor线程池的周期性执行任务代码中存在阻塞代码,定时任务不会继续执行下一次,原因就在于ScheduledThreadPoolExecutor的内部代码设计,也就是要了解它的周期性执行原理。

ScheduledThreadPoolExecutor周期性定时任务实现原理如下:任务会被包裹成ScheduledFutureTask,然后丢到队列或者直接交给线程池线程首次执行,执行后设置重新执行时间,再丢回队列,之后由线程池线程取出来再执行,周而复始。核心代码如下:

        public void run() {
            boolean periodic = isPeriodic();
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            else if (!periodic)//一次性执行
                ScheduledFutureTask.super.run();
            else if (ScheduledFutureTask.super.runAndReset()) {//周期性执行。注意下面逻辑是否执行依赖runAndReset方法返回
                //设置下次执行时间
                setNextRunTime();
                //重新把任务丢到队列
                reExecutePeriodic(outerTask);
            }
        }

当执行是周期性执行时会走ScheduledFutureTask.super.runAndReset(),而当我们的周期性业务代码出现阻塞时,runAndReset方法也会阻塞,就不会继续走到setNextRunTime();方法,也就不会有下一次执行时间的,则不会继续执行代码。看到这里我想大家也就都明白了,另外再说一点,就是如果你的业务代码运行抛出了异常,会直接中断周期性任务,解决办法就是内部try catch掉即可。

scheduleWithFixedDelay和scheduleWithFixedRate表现差异

1、scheduleAtFixedRate:

每间隔一段时间执行,分为两种情况:
1)当前任务执行时间小于间隔时间,每次到点即执行;
2)当前任务执行时间大于等于间隔时间,上一次任务执行完成后就立即执行下一次任务。相当于连续执行了。

2、scheduleWithFixedDelay

每当上次任务执行完毕后,间隔一段时间执行。不管当前任务执行时间大于、等于还是小于间隔时间,执行效果都是一样的。

总结

多线程和线线程池的使用,还是要多加小心为好,如果不确定就最好亲自验证下,以防出现意料之外的错误。现在你对ScheduledThreadPoolExecutor周期性任务代码阻塞定时任务还会继续执行下一次吗这个问题,应该有了深刻的认识了吧。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/8202.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号