核心是将加锁、执行业务、释放锁封装为不可拆分模板,业务逻辑只实现LockTask接口的run方法;模板方法executeWithLock严格按tryLock→task.run()→finally校验并安全释放顺序执行,通过Lua脚本原子比对requestId防止误释放;可选Spring AOP实现@DistributedLock零侵入。

核心在于:把加锁、执行业务、释放锁三步封装成不可拆分的模板,业务逻辑只负责“做什么”,不操心“怎么加锁”和“怎么释放”。释放逻辑被强制嵌入模板的 finally 块或等效机制中,从而规避手动遗漏、异常中断导致锁残留的问题。
用函数式接口抽象业务行为
定义一个无参无返回值的函数式接口,比如:
public interface LockTask { void run() throws Exception; }它只声明“要执行什么”,不暴露锁对象、不参与锁生命周期管理。所有加锁失败、超时、异常处理、释放动作都由模板方法内部统一控制。
模板方法内固化释放逻辑
在工具类中提供一个泛型模板方法,例如:
public该方法内部严格按以下顺序执行:
- 调用 Redisson 的 tryLock(waitTime, leaseTime) 获取锁
- 获取成功则进入 try 块:执行传入的 task.run()
- 无论 task 是否抛异常,都进入 finally 块
- finally 中判断 lock.isHeldByCurrentThread(),仅当本线程持有才 unlock()
这样,释放锁不是“建议做”,而是模板结构的一部分——只要方法执行到这一步,就必然触发校验与释放。
避免跨线程误释放的关键细节
单纯靠 finally 不够,还需防止 A 线程加锁后超时自动释放,B 线程抢到锁,A 线程又在 finally 里去 del 键。解决方案是:
- 加锁时写入唯一 requestId(如 UUID 或 ThreadId+时间戳)
- 解锁前先 get 键值,比对是否等于自己的 requestId
- 比对和 del 必须用 Lua 脚本原子执行,防止检查通过后被其他线程覆盖
这个校验逻辑也应封装进模板的 finally 流程中,对外完全透明。
配合 Spring AOP 进一步收口(可选增强)
对高频加锁方法(如支付、库存扣减),可用自定义注解 + AOP 实现零侵入:
@DistributedLock(key = "#orderNo", wait = 3, lease = 30)AOP 切面自动完成:生成 lockKey → 尝试加锁 → 执行原方法 → finally 安全释放。开发者只需关注业务,连模板方法都不用显式调用。

















