业务系统中分布式锁与异步线程池的实现及作用是什么?

2 个回答

  • 林惊羽
    天一亮,时间就不属于我了。
    ## 作用 - 在分布式系统中,多个业务节点都会运行异步线程池,本地锁(如 `ReentrantLock`)只能控制单 JVM 内的线程竞争,无法同步不同节点的任务状态。 - 分布式锁提供集群级别的资源互斥能力,确保同一个异步任务或同一业务资源在同一时刻只被一个节点的线程处理。 - 可避免线程池重复消费、重复提交任务、库存超卖、共享数据被并发修改等问题。 - 将锁的申请、释放与线程池任务生命周期绑定,可以减少无效持锁时间和无效锁请求,降低锁竞争与死锁概率。 ## 原理 ### 1. 与线程池生命周期绑定 - 不建议把加锁逻辑完全埋在任务内部,否则多个排队线程可能同时向锁服务发起请求,增加锁集群压力。 - 更合适的流程是: 1. 在线程池提交任务前,先在任务提交入口尝试获取分布式锁。 2. 获取成功后,再把任务提交给线程池执行。 3. 任务执行完成后,在统一的回调或线程池 `afterExecute` 钩子中释放锁。 - 这样可以让锁持有时间尽量与实际任务执行时间匹配,避免任务排队期间长期占用锁。 ### 2. Redis RedLock 实现思路 - 初始化多个独立 Redis 节点及连接池,文中示例采用 5 个节点。 - 提交异步任务前,线程向所有 Redis 节点申请同一把锁。 - 当超过半数节点(例如至少 3 个)加锁成功,并且总耗时未超过锁有效期时,认为加锁成功。 - 加锁成功后提交任务到线程池执行业务逻辑。 - 任务结束后调用统一解锁逻辑,释放各节点上的锁资源。 - RedLock 通过多节点投票降低单机 Redis 故障带来的风险,适合多线程、多节点批量任务竞争场景,但延迟和部署成本高于单机 Redis 锁。 ### 3. 单机 Redis 锁 - 通常基于 `SET key value NX PX 过期时间` 实现:key 不存在时写入成功,表示获得锁;value 使用唯一标识,释放时校验归属,避免误删其他线程的锁。 - 部署简单、延迟低,但存在单点故障风险,适合可靠性要求较低或有其他兜底机制的场景。 ### 4. ZooKeeper 锁 - 基于临时顺序节点和 Watcher 机制实现互斥:各线程创建临时顺序节点,序号最小的节点获得锁,其余节点监听前序节点。 - 一致性较强,但在线程池批量提交任务时,会频繁创建、删除临时节点并触发监听,增加 ZooKeeper 集群负载和锁等待延迟。 - 会话超时还可能导致临时节点被删除,从而引起锁异常释放,因此在高频异步任务场景下适配性有限。 ### 5. 关键配置 - 锁过期时间应大于任务正常执行时间,并结合任务超时时间配置,防止业务未执行完锁就提前失效。 - 对长任务可配合锁续期机制,避免业务执行期间锁自动过期。 - 释放锁必须保证只释放自己持有的锁,通常使用唯一 token 配合 Lua 脚本完成“校验 + 删除”。 - 线程池拒绝任务、任务执行异常、线程池关闭时,都要明确是否释放锁,避免资源泄漏。 - 可通过限制线程池并发数、任务排队和削峰,降低同一时刻对分布式锁的并发争抢。
    发布于 2026-10-29 13:07 •IP属地 湖北
    举报
    评论
    收藏

  • 冰儿
    我疯狂地奔跑,只想为你承担一切的伤害,我竖起了山丘,不愿你看见我丑陋的脸庞。
    ## 作用 ### 1. 异步线程池:负责单机内部的任务调度 - 通过线程复用控制单机并发线程数,避免为每个请求都创建线程,降低线程创建和销毁开销。 - 通过核心线程数、最大线程数、阻塞队列和拒绝策略控制流量,防止系统在高并发下被压垮。 - 将业务任务异步提交执行,提高请求处理能力和系统吞吐量。 - 但线程池只能控制当前 JVM、当前机器内的并发,无法感知其他应用实例。 ### 2. 分布式锁:负责多机之间的资源互斥 - 在多实例部署时,保证同一时间只有一个实例或线程可以操作某个共享资源。 - 适用于库存扣减、用户积分、余额更新等需要保证跨机数据一致性的场景。 - 通常以业务资源作为锁粒度,例如按商品 ID 加锁:`lock:product:1001`,避免所有商品串行执行。 ## 原理 ### 1. 二者协作流程 以秒杀扣减库存为例: 1. 用户请求到达任意一台应用服务器。 2. 服务先尝试获取 Redis 分布式锁。 3. 获取锁成功后,将任务提交到本地线程池。 4. 线程池中的线程执行查询库存、库存校验、扣减库存、更新数据库或缓存等逻辑。 5. 业务执行完成后立即释放锁。 6. 无论业务成功还是发生异常,都应在 `finally` 中释放锁,避免只依赖锁超时自动释放。 ### 2. 分布式锁实现要点 - 可使用 Redis 的 `SETNX` 加过期时间实现,并为每个加锁客户端生成唯一 client ID。 - 释放锁时要比对 client ID,防止 A 机器误删 B 机器持有的锁。 - 可通过 Lua 脚本保证“判断锁归属并删除锁”的原子性。 - 如果业务执行时间可能超过锁的 TTL,例如慢 SQL,需要看门狗机制自动续期。 - 对可靠性要求更高时,可以使用 Redlock 或数据库悲观锁等方式兜底。 ### 3. 线程池配置要点 以 4 核 8G 的 Java 节点为例,可参考: - `corePoolSize = 8` - `maxPoolSize = 16` - `keepAliveTime = 60s` - `workQueue = new LinkedBlockingQueue(200)` 当线程池队列已满、锁竞争激烈时,需要配置合理的拒绝策略,例如 `CallerRunsPolicy` 或自定义降级策略,同时触发限流、告警,而不是简单丢弃任务。 ### 4. 核心区别 - 线程池解决的是单机线程资源管理和异步任务调度问题。 - 分布式锁解决的是多实例之间对共享资源的互斥访问问题。 - 二者配合后,可以同时实现单机限流、异步执行以及跨机数据一致性控制。
    发布于 2026-10-28 19:04 •IP属地 上海
    举报
    评论
    收藏