为什么多线程的问题本质是“调度”?(从线程到协程 · 第1篇)
系列总纲|第1篇
本系列将从线程 → 线程池 → Reactor → 协程,讲透并发模型的演进本质。
一、先说结论
👉 多线程的问题,不是“线程多”,而是:调度不可控 + 上下文切换成本 + 资源竞争
二、为什么“调度”是核心问题?
很多人理解的调度是:
线程A执行 → 切换到线程B但这只是表象。
调度的本质 = 三件事
1. 谁来调度(控制权) 2. 怎么调度(策略) 3. 调度带来的副作用(成本)三、多线程的调度本质(问题从这里开始)
1️⃣ 调度权在操作系统(最大问题)
在多线程模型中:
线程调度 = 操作系统决定👉 这意味着:
- 什么时候执行?你控制不了
- 执行多久?你控制不了
- 什么时候被打断?你控制不了
👉 本质:调度不可控
2️⃣ 抢占式调度(不可预测)
操作系统采用:抢占式调度(Preemptive Scheduling)
👉 结果:
线程A执行中 → 被强制切走 线程B执行 → 线程A何时恢复未知👉 问题:
- 执行顺序不确定
- 逻辑容易被打断
- 并发问题难以复现
3️⃣ 上下文切换成本(调度的代价)
每一次线程切换,都要做:
保存寄存器 切换栈 进入内核态 恢复另一个线程👉 这叫:Context Switch(上下文切换)
👉 关键点:一次不贵,频繁发生 → 非常贵
4️⃣ 调度带来的最大问题:资源竞争
因为线程是“并行执行”的:
count++;在多线程下可能变成:
线程A:读取 count 线程B:读取 count 线程A:+1 写回 线程B:+1 写回(覆盖)👉 所以必须引入:synchronized / Lock / CAS
结果:
锁竞争 阻塞 死锁 性能下降四、多线程问题的完整链路(一定要理解)
多线程 ↓ 操作系统调度(不可控) ↓ 抢占执行 + 上下文切换 ↓ 多个线程同时访问共享资源 ↓ 锁 / CAS / 同步机制 ↓ 阻塞 / 死锁 / 性能问题👉 总结一句:
调度只是起点,资源竞争才是核心痛点
五、那为什么说“调度是本质问题”?
因为:
👉调度决定了所有问题是否发生
举个例子
线程A先执行 → 正常 线程B先执行 → 出问题👉 这是谁决定的?
调度👉 所以:调度的不确定性 = 并发问题的根源
六、工程上是怎么解决“调度问题”的?
重点来了(这也是后面系列的主线👇)
1️⃣ 线程池:降低“线程创建成本”
解决:
频繁创建/销毁线程但没解决:
阻塞 调度不可控 资源竞争2️⃣ Reactor:减少“无效调度”
核心思想:
不要让线程等 IO👉 只在“事件就绪”时调度
3️⃣ 协程:改变调度模型(关键突破)
核心思想:
阻塞 → 挂起👉 原来:
线程等(浪费)👉 现在:
协程等,线程去干别的👉 本质:把调度从内核搬到用户态
七、统一模型(这张图要记住)
CPU ↓ 线程(OS调度) ↓ 协程(Runtime调度) ↓ 任务(业务逻辑)👉 两级调度:
OS 调度线程 Runtime 调度协程八、一句话讲清所有并发模型
多线程:用更多线程承载更多任务 线程池:减少线程创建成本 Reactor:只在事件就绪时调度 协程:任务挂起,线程不阻塞九、面试标准回答(可直接背)
多线程的问题本质不是线程本身,而是调度带来的副作用。
在多线程模型下,调度由操作系统控制,是抢占式的,这会导致执行不可控,同时带来频繁的上下文切换和共享资源竞争问题,比如锁竞争、死锁和阻塞。
工程上一般通过线程池减少线程创建成本,通过 Reactor 模型减少无效调度,通过协程把阻塞转换为挂起,从而降低调度成本并提升线程利用率。
十、结尾
👉 到这里你会发现:
线程池只是解决了“线程创建成本”,并没有解决“调度问题”。
下一篇:
