news 2026/8/22 2:19:53

Quartz 工作模式,是“堵塞排队”还是“并发狂奔”?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Quartz 工作模式,是“堵塞排队”还是“并发狂奔”?

Quartz 是“堵塞排队”还是“并发狂奔”?

在后台系统的开发中,经常使用 Quartz 这样的框架来处理定时任务(比如每天凌晨 1 点归档数据、每 5 分钟发送一次通知)。

但你是否想过一个问题:如果任务设定每 5 分钟执行一次,但某次任务因为数据量太大,跑了 10 分钟还没跑完,第 5 分钟到来的那一刻,Quartz 会怎么做?

是乖乖等待上一次结束(堵塞)?还是不管三七二十一启动新任务(并发)?

如果没有搞清楚这个机制,且你的数据没有“中间状态”,那么重复执行的灾难就在前方等着你。

一、 Quartz 的本质:它是“发令员”,不是“监工”

首先,我们要打破一个误区:默认情况下,Quartz 不是堵塞运行的。

想象一下,Quartz 是一个调度中心的发令员,他手里拿着秒表,旁边坐着一群工人(线程池)

  1. 任务设定:每 5 分钟触发一次。
  2. 00:00:发令员看表,喊道:“时间到!去干活!” ->工人 A冲出去执行任务。
  3. 00:05:发令员看表,再次喊道:“时间到!去干活!”

关键点来了:此时,发令员根本不在乎工人 A 是否回来了。他的职责只是“按时发令”。只要旁边还有空闲的工人(线程池有空闲线程),他就会立刻派出工人 B去执行第 2 次任务。

结论:默认情况下,Quartz 是并发执行的。上一次没做完,不影响下一次的启动。

二、 灾难推演:为什么会导致“重复执行”?

并发本身不是坏事,它能保证调度的准时性。但如果你的数据处理逻辑没有中间态,灾难就会发生。

场景假设:
  • 任务逻辑:从数据库读取所有“状态=0(待处理)”的数据 -> 处理 -> 更新为“状态=2(成功)”。
  • 没有中间态:你没有把数据标记为“1(处理中)”的习惯,读到就直接开干。
  • 触发频率:每 5 分钟一次。
事故回放:
  1. 00:00(第 1 轮调度)
    • 工人 A启动。他去数据库查“状态=0”的数据,查到了100 条
    • 工人 A 开始慢慢处理,因为网络卡顿,处理这 100 条预计需要8 分钟
    • 注意:在第 5 分钟时,工人 A 还在干活,这 100 条数据在数据库里依然是“状态=0”(因为 A 还没处理完提交)。
  2. 00:05(第 2 轮调度)
    • 发令员准时发令。因为默认不堵塞,工人 B启动。
    • 工人 B 去数据库查“状态=0”的数据。
    • 恐怖的事情发生了:因为 A 还没干完,数据库里那 100 条依然是 0。
    • 工人 B也查到了这同样的 100 条数据
  3. 结果
    • 工人 A 在处理这 100 条。
    • 工人 B 也在处理这 100 条。
    • 数据被重复处理了两次!比如用户收到了两条一样的短信,或者财务报表里这笔钱加了两遍。

这就是所谓的**“无中间态下的并发事故”**。

三、 如何避免?两种思路

要解决这个问题,我们必须打破上述链条中的一环:要么让工人排队(串行),要么让数据互斥(锁)。

思路 1:给发令员立规矩(串行化 / 禁止并发)

这是最简单的方法。我们在配置 Quartz 时,给这个任务贴上一张**“请勿打扰”**的标签(在代码中通常叫DisallowConcurrentExecution)。

机制变化:

  • 00:00:发令员派出工人 A。
  • 00:05:发令员看表,时间到了。但他看到工人 A 还没回来。
  • 发令员决定:“算了,这次任务延后,或者跳过,等 A 回来再说。”

效果:系统变成了单线程堵塞模式。第 2 次任务绝对不会在第 1 次结束前启动。

  • 优点:绝对安全,完全避免了重复执行。
  • 缺点:如果任务卡死(比如 A 死锁了),后续的任务会无限期积压或停摆。
思路 2:给数据加锁(乐观锁 / 中间态)

如果你为了性能,必须让任务并发执行(比如 A 处理前 100 条,B 处理后 100 条),那你必须改造数据状态。

机制变化:

  • 引入中间态:状态链改为 0(待处理) ->1(锁定中)-> 2(成功)。
  • 00:00:工人 A 启动。他做的第一件事不是“处理”,而是**“抢占”**。他把那 100 条数据瞬间从 0 改为 1。
  • 00:05:工人 B 启动。他去查“状态=0”的数据。发现那 100 条已经是 1 了,于是他只能去查剩下的数据,或者发现没数据了就收工。

效果:任务依然是并发的,但数据被隔离了

四、 总结

  1. Quartz 默认是并发的:它不会因为上一次没做完就自动堵塞下一次,这是为了保证调度的准时性。
  2. 重复执行的根源:不是 Quartz 的错,而是**“并发执行” + “无中间态数据”** 共同导致的。两个线程同时看到了同一份未修改的数据。
  3. 怎么选
    • 怕麻烦、数据量小:直接配置 Quartz“禁止并发执行”(串行模式)。这是最稳妥的方案,宁可任务延时,也不要数据出错。
    • 数据量巨大、追求高吞吐:保持并发,但必须引入**“处理中”的状态,或者使用代码锁**来控制抢占逻辑。

所以,回到问题:前后两次是否会导致任务重复执行?

答案是:如果不做特殊配置且没有中间态保护,一定会!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/21 18:30:44

NuttX SVC系统调用机制深度解析

NuttX SVC系统调用机制深度解析 概述 NuttX作为一个实时操作系统,提供了完整的系统调用机制来实现用户空间和内核空间的安全隔离。本文将深入解析NuttX中SVC(Supervisor Call)系统调用的工作原理,从硬件异常处理到高级API调用的完整流程。 1. SVC异常处理基础 1.1 异常入…

作者头像 李华
网站建设 2026/8/21 13:32:52

防火墙配置:掌握 iptables、firewalld 等工具的使用与管理

防火墙配置:掌握 iptables、firewalld 等工具的使用与管理 在现代服务器安全体系中,防火墙是最基础、最关键的安全屏障之一。无论是传统的 iptables,还是更现代的 firewalld,都承担着“限制访问、减少攻击面、保护系统”的核心职责…

作者头像 李华
网站建设 2026/8/21 23:35:55

【C2000系列DSP的Bootloader详解】实现过程、流程图与示例代码

【C2000系列DSP的Bootloader详解】实现过程、流程图与示例代码 Bootloader是嵌入式系统启动的“领航员”,负责在芯片上电后完成初始化、程序加载等核心工作,是C2000系列微控制器正常运行的基础。本文基于TMS320F28003x技术参考手册第四章“ROM Code and Peripheral Booting”…

作者头像 李华
网站建设 2026/8/21 4:05:45

【C2000系列DSP的Bootloader详解】如何利用脚本自动生成Hex/Bin/S19文件

【C2000系列DSP的Bootloader详解】如何利用脚本自动生成Hex/Bin/S19 专为TI C2000系列C28x内核芯片设计,实现bootloader.out和user_app.out文件的Hex/Bin/S19转换,以及转换后Hex/Bin/S19文件的合规合并,生成可直接用于Uniflash烧录的文件。 OutconvertHex.py, 可以生成he…

作者头像 李华