news 2026/6/4 7:42:59

分布式线程池“打架”?Redisson 锁竞争闭环实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
分布式线程池“打架”?Redisson 锁竞争闭环实战

分布式线程池“打架”?Redisson 锁竞争闭环实战

前言

分布式部署后,单机线程池的状态隔离会变成集群级协调问题。多个节点同时执行有状态任务时,可能出现重复消费、资源抢占、拒绝策略频繁触发和 CPU 飙升等问题。

本文围绕分布式线程池的锁竞争闭环,分析如何使用 Redisson 分布式锁、WatchDog 续期和任务状态管理,保证集群任务执行的互斥性与可恢复性。

一、底层原理

1.1 核心机制

在单机环境下,ThreadPoolExecutor的状态是内存级的,大家互不干扰。

一旦变成微服务集群,三台机器各自维护各自的线程池。

任务分发过来,谁抢到谁执行。

这时候,如果任务本身有状态依赖(比如只能有一个节点在写库),那就麻烦了。

我们需要一个分布式的“交通指挥员”。

Redisson 就是那个拿着大喇叭的交警。

它基于 Redis 实现了分布式锁,核心是RLock

看门狗(WatchDog)机制是它的灵魂。

业务逻辑还没跑完,锁的过期时间会自动续命。

这就避免了“锁还没释放,进程先挂了”的尴尬。

sequenceDiagram participant NodeA as 节点 A participant NodeB as 节点 B participant Redis as Redis 服务器 participant WatchDog as 看门狗线程 NodeA->>Redis: 尝试获取锁 (tryLock) Redis-->>NodeA: 获取成功 NodeA->>NodeA: 执行业务逻辑 NodeA->>WatchDog: 启动续期任务 WatchDog-->>Redis: 自动延长 TTL NodeA->>Redis: 释放锁 (unlock) Redis-->>NodeB: 通知锁可用 NodeB->>Redis: 尝试获取锁

设计优势很明显。

自动续期解决了业务执行时间不可控的问题。

重入性保证了同一个线程可以多次获取同一把锁。

1.2 与同类方案的对比

咱们再横向对比一下,别盲目选技术。

方案实现方式优点缺点适用场景
RedissonRedis + Lua 脚本自动续期,API 丰富,集成简单依赖 Redis 高可用绝大多数分布式锁场景
Zookeeper临时顺序节点强一致性,CP 模型性能稍低,运维成本高对一致性要求极高的金融场景
数据库锁唯一索引/版本号无需额外组件性能差,易死锁低频、非核心业务

对于咱们 Java 微服务来说,Redisson 是性价比之王。

除非你有特殊的强一致性需求,否则别折腾 ZK。

二、快速上手

别整那些复杂的配置,先来个 Hello World。

三分钟,让你体验一把“独占”的感觉。

// 引入 Redisson 依赖 (Maven) // <dependency> // <groupId>org.redisson</groupId> // <artifactId>redisson-spring-boot-starter</artifactId> // <version>3.23.0</version> // </dependency> public class LockDemo { // 注入 Redisson 客户端 // 假设你已经配置好了 Spring Boot Starter @Autowired private RedissonClient redissonClient; public void runTask() { // 定义锁的键名,最好带上业务前缀 String lockKey = "lock:task:exclusive"; // 获取分布式锁对象 RLock rLock = redissonClient.getLock(lockKey); // 尝试获取锁 // 参数 1:等待锁的最长时间(秒) // 参数 2:锁自动过期的时间(秒) boolean isLocked = false; try { // 如果 5 秒内没抢到,就放弃 isLocked = rLock.tryLock(5, 30, TimeUnit.SECONDS); if (isLocked) { System.out.println("节点 " + getNodeId() + " 抢到了锁,开始干活"); // 模拟业务逻辑 doBusinessLogic(); } else { System.out.println("节点 " + getNodeId() + " 没抢到,去喝杯咖啡"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("获取锁被中断了"); } finally { // 一定要在 finally 里释放锁 // 防止业务异常导致锁无法释放 if (isLocked && rLock.isHeldByCurrentThread()) { rLock.unlock(); System.out.println("锁已释放"); } } } private String getNodeId() { return "Node-" + System.currentTimeMillis() % 1000; } private void doBusinessLogic() { // 模拟耗时操作 try { Thread.sleep(2000); } catch (Exception e) {} } }

看,就这么简单。

tryLock是核心,别用lock,那个会无限阻塞。

生产环境必须设超时时间。

三、核心 API / 深水区

3.1 核心方法速查

Redisson 的 API 设计得很人性化,像操作本地锁一样。

方法说明注意事项
tryLock(waitTime, leaseTime, unit)尝试加锁必须设置 leaseTime,否则看门狗不生效
unlock()释放锁只有持有锁的线程才能释放
isLocked()判断是否被锁只能判断当前 Redis 状态,非强一致
isHeldByCurrentThread()判断当前线程是否持有释放锁前务必检查,防止误删别人的锁

3.2 生产级配置

线上环境,配置得细致点。

特别是超时控制和异常处理。

@Configuration public class RedissonConfig { @Bean public RedissonClient redissonClient() { Config config = new Config(); // 使用单机模式,生产请用集群模式 config.useSingleServer() .setAddress("redis://127.0.0.1:6379") // 连接池配置 .setConnectionPoolSize(10) .setConnectionMinimumIdleSize(5); return Redisson.create(config); } }

异常处理方面,一定要捕获InterruptedException

不然线程池里的线程被中断了,状态就脏了。

3.3 高级定制

有些场景,需要红锁(RedLock)。

就是同时在多个 Redis 节点上加锁。

虽然 Redisson 支持,但我不建议轻易用。

除非你的业务真的不能容忍主从切换带来的锁丢失。

大多数时候,单机 Redis 的锁已经够用了。

四、实战演练

咱们来个真实的场景。

多节点抢占“每日报表生成”任务。

只能有一个节点在跑,跑了就更新状态,别重复生成。

@Component public class ReportScheduler { @Autowired private RedissonClient redissonClient; @Autowired private ReportService reportService; // 模拟线程池任务 @Scheduled(cron = "0 0 1 * * ?") public void generateDailyReport() { String lockKey = "lock:report:generate"; RLock lock = redissonClient.getLock(lockKey); boolean acquired = false; try { // 等待 3 秒,锁有效期 60 秒 acquired = lock.tryLock(3, 60, TimeUnit.SECONDS); if (acquired) { // 双重检查,防止并发下状态已更新 if (!reportService.isReportGeneratedToday()) { System.out.println("开始生成今日报表..."); reportService.createReport(); System.out.println("报表生成完毕"); } else { System.out.println("今日报表已存在,跳过"); } } } catch (Exception e) { System.err.println("任务执行异常:" + e.getMessage()); } finally { if (acquired && lock.isHeldByCurrentThread()) { lock.unlock(); } } } }

结果分析:

三台服务器同时触发定时任务。

只有一台能拿到锁,进入if块。

其他两台拿到false,直接打印跳过。

资源竞争闭环完成。

五、避坑指南与最佳实践

踩过的坑,都是真金白银换来的。

💡技巧:锁的粒度要细。

别搞个lock:all把整个系统锁死。

按业务 ID 分片,lock:report:user:123

⚠️警告:别在锁里做远程 RPC 调用。

万一对方超时,你的锁就延期了,甚至死锁。

推荐:锁过期时间要大于业务最大执行时间。

估算好最坏情况,留足余量。

⚠️警告:Redis 主从切换可能导致锁丢失。

这是 CAP 理论的妥协,业务上要设计幂等性。

六、综合实战演示

最后,给一套精简、闭环的综合实战代码。

这是一个通用的分布式任务执行器。

@Component public class DistributedTaskExecutor { @Autowired private RedissonClient redissonClient; /** * 执行带锁的分布式任务 * * @param taskName 任务名称,用于生成锁 Key * @param runnable 具体任务逻辑 * @param waitTime 等待锁时间(秒) * @param leaseTime 锁持有时间(秒) */ public void executeWithLock(String taskName, Runnable runnable, long waitTime, long leaseTime) { String key = "distributed:lock:" + taskName; RLock lock = redissonClient.getLock(key); boolean isLocked = false; try { // 尝试获取锁 isLocked = lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); if (isLocked) { System.out.println("[任务 " + taskName + "] 获取锁成功,执行中..."); runnable.run(); } else { System.out.println("[任务 " + taskName + "] 获取锁失败,其他节点正在执行"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("[任务 " + taskName + "] 线程被中断"); } catch (Exception e) { System.err.println("[任务 " + taskName + "] 业务执行异常: " + e.getMessage()); } finally { // 安全释放锁 if (isLocked && lock.isHeldByCurrentThread()) { lock.unlock(); System.out.println("[任务 " + taskName + "] 锁已释放"); } } } }

调用方式:

@Autowired private DistributedTaskExecutor executor; public void startJob() { executor.executeWithLock("order_sync", () -> { // 这里写具体的业务代码 System.out.println("正在同步订单数据..."); }, 5, 30); }

七、总结

分布式锁不是银弹,但它是解决资源竞争的必要手段。

Redisson 把复杂的事情变简单了。

记住三点:

  1. tryLock必须设超时。
  2. unlock必须在finally里。
  3. 业务逻辑要幂等。

把锁用好,线程池不再“打架”,线上稳如老狗。

散会。

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

想要对接广东北交所上市财务应对辅导机构有哪些可靠联系渠道

随着北交所对专精特新企业支持力度持续提升&#xff0c;广东作为国内制造重镇&#xff0c;越来越多本土成长型实体企业启动了北交所上市规划。财务合规是北交所上市审核的核心门槛&#xff0c;不少企业在寻找专业财务应对辅导机构时&#xff0c;常常困惑于哪些渠道更可靠&#…

作者头像 李华
网站建设 2026/6/4 7:34:58

PHP图形验证码技术实现

PHP图形验证码与验证码技术实现验证码是防止自动化攻击的常用手段。从简单的数字验证码到行为验证&#xff0c;PHP都能实现。今天说说各种验证码的实现方式。用GD库生成图片验证码是最传统的方式。核心思路是生成随机字符&#xff0c;画到图片上&#xff0c;添加干扰线和噪点来…

作者头像 李华
网站建设 2026/6/4 7:28:57

开发2天,测试2个月:AI代码让谁偷懒了?

开发2天&#xff0c;测试2个月&#xff1a;AI代码生成正在把验证成本甩给谁&#xff1f;一、一个真实的案例&#xff1a;AI生成的Todo App二、AI生成的代码到底差在哪里&#xff1f;2.1 表层正确性&#xff1a;语法、编译、主路径2.2 深层健壮性&#xff1a;边界、异常、并发、…

作者头像 李华