并发编程里的"堵车"与"红绿灯":死锁、活锁与两种锁策略(乐观锁、悲观锁)

搞后端或者数据库开发,永远绕不开"资源争抢"这个问题。

今天把这几个老生常谈的概念------死锁、活锁、悲观锁、乐观锁,放在一起捋一捋。前两个是"事故现场",后两个是"交通规则"。一起学习一下


一、 事故现场:当线程打起来了

1. 死锁 (Deadlock):互不相让

这是最常见的"事故"。说白了就是:也就是你也动不了,我也动不了,大家都耗着。

真实场景 : 你有两个资源:OrderTable(订单表)和 StockTable(库存表)。

  • 线程 A:锁住了 OrderTable,准备去扣减 StockTable
  • 线程 B:锁住了 StockTable,准备去写入 OrderTable

结果就是:A 拿着 B 想要的东西,B 拿着 A 想要的东西,谁都不撒手。

代码模拟(伪代码)

csharp 复制代码
// 错误的加锁顺序导致死锁
​
// 线程 1
async function transactionA() {
  await lock('Order'); // 拿到了 Order 锁
  await sleep(100);    // 稍微慢一点,等线程 2 锁住 Stock
  await lock('Stock'); // 此时卡住,因为 Stock 被线程 2 锁了
}
​
// 线程 2
async function transactionB() {
  await lock('Stock'); // 拿到了 Stock 锁
  await sleep(100);
  await lock('Order'); // 此时卡住,因为 Order 被线程 1 锁了
}

怎么解? 最简单的解法是定规矩 :所有人必须按照相同的顺序拿锁。比如规定"必须先拿 Order 再拿 Stock",死锁瞬间就解了。

2. 活锁 (Livelock):过分礼让

活锁比较隐蔽,它不会卡死,但就是干不成事。

真实场景: 两人在走廊迎面相遇。

  • 你往左躲,他也往左躲(撞)。
  • 你赶紧往右躲,他也往右躲(又撞)。
  • 你们俩一直在动,CPU 也就是一直在忙(出汗了),但谁也没过去。

代码里的表现: 通常发生在处理消息队列或者重试机制里。 比如一个任务处理失败了,系统立刻重试,又失败,又立刻重试... 资源一直在消耗,日志一直在打,但任务永远处于"处理中"。

怎么解? 引入随机性。以太网的"指数退避算法"就是这个原理:撞了之后,不要立刻重试,你等 10ms,我等 50ms,错峰出行。


二、 交通规则:怎么防止出事故?

为了防止上面那种乱套的情况,我们需要锁。根据"心态"的不同,分成了悲观乐观两派。

1. 悲观锁 (Pessimistic Locking)

心态:"这世界坏人多,总有人想抢我的数据。也就是我操作的时候,谁也别想动!"

做法: 在查数据的时候,直接把数据锁死,直到我提交事务,别人才能查或改。

2. 乐观锁 (Optimistic Locking)

心态:"这世界还是好人多,大部分时候没人跟我抢。我就先跑着,最后检查一下就行。"

做法 : 我不锁数据,大家都随便读、随便改。但是!我在更新的那一瞬间,会检查一下: "这数据自从我读走之后,有没有被人改过?"

实现方式(CAS / 版本号) : 给表加一个 version 字段。

ini 复制代码
-- 1. 先查出来,假设拿到 version = 1
SELECT id, stock, version FROM goods WHERE id = 1;
​
-- 2. 内存里计算 stock - 1
​
-- 3. 更新时,带上版本号做条件
UPDATE goods 
SET stock = stock - 1, version = version + 1 
WHERE id = 1 AND version = 1; 
-- 关键在这里!如果 version 已经被别人改成 2 了,这条 SQL 影响行数就是 0,更新失败。
  • 优点:快!没有锁等待,大家都能读,并发能力强。
  • 缺点:如果竞争真的很激烈(比如抢火车票),会一直失败重试,CPU 可能会飙高。

如果你觉得这篇文章有帮助,欢迎关注我的 GitHub,下面是我的一些开源项目:

Claude Code Skills (按需加载,意图自动识别,不浪费 token,介绍文章):

全栈项目(适合学习现代技术栈):

  • prompt-vault - Prompt 管理器,用的都是最新的技术栈,适合用来学习了解最新的前端全栈开发范式:Next.js 15 + React 19 + tRPC 11 + Supabase 全栈示例,clone 下来配个免费 Supabase 就能跑
  • chat_edit - 双模式 AI 应用(聊天+富文本编辑),Vue 3.5 + TypeScript + Vite 5 + Quill 2.0 + IndexedDB
相关推荐
骑着bug的coder3 小时前
第11讲:主从复制与读写分离架构
后端·mysql
小王和八蛋3 小时前
前端存储与离线应用实战:Cookie、LocalStorage、PWA 及 Service Worker 核心知识点
前端·javascript
love_summer3 小时前
代码中的“留白”艺术:Python空语句pass的设计哲学与最佳实践
后端
JarvanMo3 小时前
终极指南:在 Flutter 中通过 sign_in_with_apple 实现 Apple 登录
前端
Learner3 小时前
Python异常处理
java·前端·python
tao3556673 小时前
VS Code登录codex,报错(os error 10013)
java·服务器·前端
6***83053 小时前
SpringBoot教程(三十二) SpringBoot集成Skywalking链路跟踪
spring boot·后端·skywalking
军军君013 小时前
Three.js基础功能学习七:加载器与管理器
开发语言·前端·javascript·学习·3d·threejs·三维
JarvanMo3 小时前
情迷服务器驱动 UI:我在 Flutter 开发中的爱与哀愁
前端