← 每日面试题 学习看板

用@Scheduled执行定时任务,如何避免集群的并发问题

面试题/01实践场景题/实践场景题目/Java生态与编程/用@Scheduled执行定时任务,如何避免集群的并发问题.md

面试记录 2026-10-14

题目:用 @Scheduled 执行定时任务,如何避免集群的并发问题?

难度:medium

标签:practice/java, java/concurrency, spring/scheduling

评分:⭐⭐⭐⭐☆(4/5)

我的回答

首先,集群了为什么要使用单机的计划任务,这里应该去使用分布式调度任务平台,比如 xxljob 之类的;

如果一定要用单机计划任务那你就要考虑任务执行的幂等性了。首先确认这个任务是否是幂等的,它执行多次是否会有不同的影响。如果没有,那么就简单的控制做一个锁。这个任务在执行节点时候,通过类似于 Redis 锁这种分布式锁去控制,这个任务已经执行了,在这个节点已经执行了。执行过后,其他执行中的其他节点在执行的时候判定一下,如果当前已经在执行或者已经执行完了,其他节点不再二次执行,防止并发。

核心其实有很多可以处理的,比如说任务状态、任务记录,然后换成 Redis 锁,都可以处理这个问题。比如执行,执行完任务,在这个节点就查一条日志,下次执行前就查询数据库是否有这个日志。其他任务同时间做一个锁,执行的时候就是,有这个日志大家就不执行了,没这个日志的,就第一个就执行,就结束了,就可以控制这个问题。

而第一个要给计划任务加锁,防止并发。第二个就是做密等。当然不做密等的话,就做控制,控制限定,然后判定执行了,当前这个时间节点要求执行的执行了,其他就不执行。就能解决相应的问题。

点评

优点:

不足:

补充:

完整讲解

第一部分:知识重点剖析

核心考点定位:考察"单机调度"到"分布式调度"的架构演进,以及分布式锁的工程化细节。

知识点拆解:

  1. @Scheduled 的单机局限:
  1. 分布式锁三大陷阱:
  1. xxl-job 核心机制:
  1. 方案对比:
  1. 兜底策略:即使锁失效,业务幂等(唯一索引 + 状态机)要能兜住

第二部分:考察方向与延伸递进

考察方向:

延伸递进面试题:

  1. Redis 分布式锁的 TTL 怎么设置合理?
  1. xxl-job 分片广播和 Quartz JDBC store 有什么区别?
  1. 如果任务执行时间超过锁 TTL,怎么保证不并发?

第三部分:面试者参考回答(三步法口语化,300-400 字)

【一句话结论】

面试官您好,单机 @Scheduled 在集群下必然并发,首选上分布式调度平台(xxl-job);如果一定要在 @Scheduled 上救场,就用"分布式锁 + 业务幂等"组合拳。

【三点展开】

第一,先判断该不该用 @Scheduled:如果是生产级定时任务(有重试、告警、分片需求),直接上 xxl-job,它有分片广播(多节点分片执行)、阻塞策略(覆盖/丢弃/排队)、路由策略(轮询/一致性哈希)、失败重试、执行日志,单机 @Scheduled 缺的功能它都有。

第二,如果一定要 @Scheduled + 分布式锁:用 Redisson RLock 而不是裸 Redis SETNX,Redisson 有看门狗机制(30s 自动续期)解决 TTL 陷阱,value 用 UUID + CAS 校验防误删。锁的粒度要按业务场景选:全局唯一锁(任务级)、hash 分片锁(数据级)。锁竞争高时降级到数据库 CAS(UPDATE task_lock SET locked_by=? WHERE status='free' AND expire_time>?)。

第三,锁失效的兜底是业务幂等:任务处理的数据写入用唯一索引(unique key on biz_id),状态机用乐观锁 SQL(UPDATE ... WHERE status=?)。即使锁失效并发执行了,业务层也能兜住不重复处理。锁是"防并发",幂等是"防重复",两者组合才完整。

【一句边界】

当然,如果是分钟级以上的低频任务,甚至可以用"数据库唯一索引 + 时间窗口"做最简锁(INSERT INTO task_log(task_name, exec_time) VALUES(?, ?) ON DUPLICATE KEY UPDATE ...),不用引入 Redis 依赖——KISS 原则,能用数据库解决的别引入新组件。