ShedLock如何通过锁名查询锁状态,解决长任务REST调用阻塞问题
问题根因说明
你遇到的锁提前释放问题,本质是@Async注解会将任务提交到线程池后立即返回,原调用线程的执行生命周期结束后就会触发锁释放逻辑,不会等待异步线程的任务执行完成。
适配方案
方案1:锁状态预检查实现非阻塞触发
这是最贴合你需求的实现方式:
- 如果你使用的是Spring体系下的
LockingTaskExecutor实现,可直接通过关联的LockRegistry获取锁实例,调用tryLock(0, TimeUnit.SECONDS)非阻塞预占锁,获取失败则直接返回"任务正在运行" - 预占锁成功后,新开异步线程执行业务逻辑,锁的释放操作绑定到异步线程的
finally代码块,避免锁提前释放 - 原REST调用线程提交完异步任务后直接返回触发结果
示例代码逻辑:
@Resource private LockRegistry lockRegistry; @Resource private TaskExecutor asyncTaskExecutor; public Result triggerTask(String lockKey, LockingTaskExecutor.TaskWithResult task) { // 非阻塞预检查锁状态 Lock lock = lockRegistry.obtain(lockKey); if (!lock.tryLock()) { return Result.fail("当前任务已在执行,无需重复触发"); } try { // 异步提交任务执行 asyncTaskExecutor.execute(() -> { try { task.call(); // 这里可自行持久化TaskResult供后续查询 } finally { // 异步任务执行结束后释放锁 lock.unlock(); } }); return Result.success("任务触发成功"); } catch (Exception e) { // 异步提交失败时释放预占的锁,避免死锁 lock.unlock(); throw new BusinessException("任务触发失败", e); } }
如果你的LockingTaskExecutor未暴露锁操作接口,可自行维护一个ConcurrentHashMap<String, AtomicBoolean>来记录锁的运行状态,状态更新操作要和锁的获取、释放逻辑保持原子性,避免状态不一致。
方案2:改造executeWithLock支持异步模式
无需依赖@Async注解,直接修改锁执行器的逻辑:
- 给
executeWithLock方法新增async布尔类型入参 - 当
async为true时,获取锁成功后将任务提交到独立的异步线程池执行,锁的释放逻辑绑定到异步任务的结束节点 - 原调用线程提交完任务后直接返回任务唯一标识,可通过该标识后续查询任务执行状态
方案3:引入分布式任务调度组件(可选)
如果你的场景需要支持任务状态持久化、重试、失败告警等扩展能力,可引入xxl-job、Quartz等任务调度组件:
- 给任务配置唯一幂等键,调度组件天然保证同一幂等键的任务同时仅存在一个运行实例
- REST接口仅负责提交任务调度请求,无需等待执行结果,完全避免阻塞问题
内容的提问来源于stack exchange,提问作者user2043662
相关产品推荐
相关产品推荐

