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

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

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


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

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
相关推荐
一 乐2 小时前
智慧医药|基于springboot + vue智慧医药系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端
CC码码2 小时前
告别杂乱数字:用 Intl.NumberFormat 打造全球友好的前端体验
前端·javascript·面试
Tony Bai2 小时前
Goroutine “气泡”宇宙——Go 并发模型的新维度
开发语言·后端·golang
王中阳Go2 小时前
攻克制造业知识检索难题:我们如何用Go+AI打造高可用RAG系统,将查询效率提升600%
人工智能·后端·go
游浪踏2 小时前
006_prompt
后端·openai
悟空码字2 小时前
SpringBoot接口防抖大作战,拒绝“手抖”重复提交!
java·spring boot·后端
妮妮喔妮2 小时前
Webpack和Vite优化的区别
前端·webpack·node.js
与遨游于天地2 小时前
Spring 的10个核心能力,对框架开发的启示
java·后端·spring
广州华水科技3 小时前
单北斗GNSS在大坝形变监测中的应用与性能分析
前端