Spring Boot如何实现定时任务与接口方法的互斥执行?
解决方案
可以通过Java并发工具类结合Spring特性实现需求,核心思路是用锁控制两个方法的互斥,同时用原子变量过滤second的重复请求。
核心实现代码
在你的控制器/调度类中定义并发控制成员变量并修改方法逻辑:
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class YourController { // 控制first和second互斥的锁 private final ReentrantLock dbOperationLock = new ReentrantLock(); // 标记second是否正在执行,过滤重复请求 private final AtomicBoolean isSecondProcessing = new AtomicBoolean(false); @Scheduled(cron = "0 * * * * *") public void first() { // 尝试获取锁,失败则直接跳过本次执行(不等待、不排队) if (!dbOperationLock.tryLock()) { return; } try { // 你的数据库操作逻辑 } finally { // 必须在finally中释放锁,防止异常导致锁泄漏 dbOperationLock.unlock(); } } @RequestMapping(value = "/second", method = RequestMethod.POST) public void second() { // CAS操作:只有当前无second执行时,才标记为正在执行,否则直接返回 if (!isSecondProcessing.compareAndSet(false, true)) { return; } try { // 阻塞等待锁释放(如果first正在运行,会等它执行完) dbOperationLock.lock(); try { // 你的数据库操作逻辑 } finally { dbOperationLock.unlock(); } } finally { // 无论是否异常,都标记second执行完成 isSecondProcessing.set(false); } } }
逻辑说明
first()定时任务:
使用tryLock()尝试获取锁,获取失败直接返回,完全符合"second执行期间阻止first运行,不等待不排队"的需求;获取成功则执行数据库操作,最后在finally中释放锁。second()接口:
- 通过
AtomicBoolean.compareAndSet做CAS校验,确保同一时间只有一个second请求被处理,重复请求直接忽略; - 使用
lock()阻塞等待锁,如果此时first正在运行,会等待first执行完释放锁后再执行自身逻辑; - 执行完成后,在finally块中重置原子变量和释放锁,避免异常导致资源泄漏。
- 通过
注意事项
- 确保类是Spring单例(默认@RestController、@Component等注解均为单例),否则锁和原子变量会失去作用;
- 锁的范围尽量缩小,只包裹数据库操作核心逻辑,减少锁持有时间以提升性能;
- 若需要给前端反馈(如重复请求被忽略),可在return前添加响应逻辑,比如返回
ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("操作正在处理,请稍后再试")。
内容的提问来源于stack exchange,提问作者Wes
相关产品推荐
相关产品推荐

