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

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

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


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

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
相关推荐
前进的搬砖er10 分钟前
4.three.js的几何体、纹理、光源、材质、uv属性 讲义
前端
前端毕业班23 分钟前
uni-app 小程序支持 teleport 了
前端·javascript·vue.js
易协同低代码43 分钟前
通达OA核心类库TD类深度解析
后端
Gopher_HBo1 小时前
Go语言学习笔记(十八)Gin处理Session
后端
梨子同志1 小时前
qiankun
前端
梨子同志1 小时前
Nginx
前端
梨子同志1 小时前
NestJS
前端
梨子同志1 小时前
Next.js
前端
谭光志2 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
Hilaku2 小时前
前端架构师的克制:什么时候该坚决对新技术说不?
前端·javascript·程序员