用Semaphore控制并发数+AtomicInteger做额度校验的关键在于分桶隔离:每个商户独立信号量、独立计数器;首次访问懒加载初始化;结算前需信号量席位校验+原子金额扣减双重校验;异常或超时必须精准释放;额度变更需清空重置,释放时同步更新计数器与信号量。

用 Semaphore 控制并发数 + 原子计数器(如 AtomicInteger)做额度校验,关键不在“加锁”,而在“分桶隔离”——每个商户独立配额、独立信号量、独立计数器,避免跨商户争抢资源。
按商户 ID 动态创建隔离单元
不要全局一个 Semaphore 或一个计数器。为每个商户(例如 merchantId=1001)维护专属资源:
- 一个
ConcurrentHashMap<long semaphore></long>缓存各商户的信号量,初始许可数 = 该商户当前可用结算额度(如 50) - 一个
ConcurrentHashMap<long atomicinteger></long>记录实时已占用额度(用于幂等回滚和瞬时超卖防护) - 首次访问某商户时懒加载初始化,避免预热开销
结算前执行两级校验:信号量 + 原子扣减
单纯靠 Semaphore 的 acquire() 无法保证“扣减值 = 实际订单金额”,需组合原子操作:
- 先 tryAcquire(1),获取一个“结算席位”(防并发超限)
- 再用
atomic.getAndAdd(amount)尝试累加本次占用额度 - 若累加后总占用 > 商户额度上限,则 rollback:释放信号量 + 原子减回,并拒绝结算
- 成功则进入后续库存/支付流程
异常与超时必须触发精准释放
结算链路长(调库存、锁优惠、生成订单),任何环节失败都可能导致“占着额度不释放”:
- 所有 acquire() 必须配对使用 try-finally,finally 中显式 release()
- 建议封装成
tryAcquireWithRollback(merchantId, amount)工具方法,内部统一处理 acquire/release 和原子计数器增减 - 对外暴露的结算接口加上超时控制(如
tryAcquire(3, SECONDS)),避免线程长期阻塞
额度动态刷新要穿透隔离层
商户额度可能被运营后台调整,或结算完成释放后需更新:
- 额度变更时,不是简单 replace 计数器,而是:先
getAndSet(0)清空已占用,再重置信号量许可数(semaphore.drainPermits(); semaphore.release(newQuota)) - 释放额度时(如订单取消),先原子减去对应 amount,再 release() 对应数量的信号量许可
- 推荐用
StampedLock或 CAS 循环保障“读额度-扣减-写回”三步原子性,避免中间态不一致
不复杂但容易忽略:信号量只管“并发数”,原子计数器才管“金额精度”。两者配合,才能让多商户购物车在高并发下既不超限,也不误杀合法请求。


















