javascript异步锁通过promise队列和状态标记实现共享资源的串行化访问,避免数据竞争;需用try/finally确保释放、按资源粒度拆分锁,并配合业务校验使用。

JavaScript 中异步锁防止数据竞争,核心是让多个并发的异步操作**串行化访问共享资源**,而不是靠“阻塞线程”(JS 本就没有多线程)。它利用 Promise 队列 + 状态标记,在事件循环中调度执行顺序,确保同一时刻最多只有一个操作进入临界区。
用锁把并发请求变成排队执行
没有锁时,多个 fetch 或数据库写入可能几乎同时开始、交错修改同一个变量或状态,造成丢失更新(比如两个请求都读到 count = 5,各自加 1 后都写回 6);加上锁后,第二个请求会自动等待第一个完成后再读取最新值——本质是把“竞态窗口”从并行压缩为串行。
- 锁内部维护一个
isLocked标志和一个等待队列(如Promise.resolve()链或queue.push(resolve)) - 每次
await lock.acquire()时:若未被占用,立即返回;否则挂起当前 async 函数,加入队列等待通知 -
unlock()触发时,只唤醒队列中第一个等待者,形成 FIFO 队列行为
必须配合 try/finally 确保锁一定释放
异步操作可能抛错(如网络超时、解析失败),如果只在成功路径调用 unlock(),锁就会永久卡住,后续所有请求无限等待——这就是“死锁”的 JS 版本。正确写法是:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 用
try { await lock.acquire(); ... } finally { lock.release(); } - 或使用支持自动释放的语法糖(如
async-mutex的runExclusive) - 避免在锁持有期间做同步耗时操作(如大数组遍历),否则会拖慢整个队列
锁粒度要细,别一把锁管全局
用一个全局 Mutex 保护所有用户数据,会导致用户 A 修改自己的头像时,用户 B 查看订单也被卡住——这不是安全,是性能灾难。实际应按资源维度拆分:
- 对单个用户 ID 加锁:
const userLock = locks.get(userId) || new Mutex() - 对 API 路径加锁(防重复提交):
const submitLock = mutexes[formId] - 用 Semaphore 控制并发数而非完全串行(例如最多 3 个上传任务并行)
它不解决所有并发问题,只是关键一环
锁能防止“同时写”,但不能替代业务逻辑校验。例如库存扣减,仍需在临界区内二次检查:“扣前查库存是否 ≥ 所需数量”,因为锁只保证顺序,不保证外部状态不变。再比如前端防重复点击,锁可配合按钮禁用 + loading 状态,形成端到端防护。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










