线程五种状态
线程的状态
1. 通用五态模型 (The 5-State Model)
这是操作系统层面的标准定义:
1. 新建 (New)
- 状态描述:线程对象已经被创建(比如 C++ 中
new Thread或 Javanew Thread()),但还没有被启动,还没有分配 CPU 资源,也没有进入调度队列。 - 动作:执行
start()后进入就绪态。
2. 就绪 (Ready / Runnable)
- 状态描述:线程万事俱备,只欠 CPU。它已经在调度器的"就绪队列"里排队了,随时可以运行,但 CPU 时间片还没轮到它。
- 关键点:在多核 CPU 下,可能有多个线程同时处于就绪态。
3. 运行 (Running)
- 状态描述:线程获得了 CPU 时间片,正在执行代码逻辑。
- 转换: 如果时间片用完 => 回到 就绪态。 如果等待 I/O 或锁 => 进入 阻塞态。 如果代码执行完毕 => 进入 终止态。
4. 阻塞 (Blocked / Waiting)
- 状态描述:线程暂停运行,让出 CPU。因为它在等待某个"事件"发生,即使给它 CPU 它也干不了活。
- 常见原因: 等待 I/O:读磁盘、等网卡数据包。 等待锁:抢
mutex没抢到。 主动休眠:调用了sleep()或wait()。 - 恢复:当事件发生(数据来了、锁释放了),线程不会直接回到 Running,而是先回到 Ready(就绪) 重新排队。
5. 终止 (Terminated / Dead)
- 状态描述:线程的任务执行完毕(正常返回)或发生异常强行退出。其生命周期结束,操作系统回收其资源(栈空间等)。
2. 状态转换图解 (逻辑流)
为了方便记忆,请脑补这个流转过程:
graph LR
New(新建) -->|start| Ready(就绪)
Ready -->|调度器选中| Running(运行)
Running -->|时间片用完| Ready
Running -->|等待IO/锁| Blocked(阻塞)
Blocked -->|事件完成| Ready
Running -->|执行结束| Terminated(终止)
重点考点:
- Running =》 Blocked:是线程主动请求(读取文件)或被动限制(抢锁失败)。
- Blocked =》 Running:这是不可能的! 阻塞结束后,必须先回 Ready 排队,不能插队直接运行。
3. Linux 视角下的线程状态 (实战)
如果你在 Linux 下使用 top或 ps命令查看线程(LWP),你会看到 STAT列,它们对应着具体的操作系统状态:
| 符号 | 状态名称 | 对应通用模型 | 解释 |
|---|---|---|---|
| R | Running/Runnable | 运行 或 就绪 | 正在 CPU 上跑,或者在队列里排队。Linux 不区分 Running 和 Ready,都算 R。 |
| S | Sleep (Interruptible) | 阻塞 | 浅度睡眠。等待网络数据、时钟等。可以被信号唤醒(比如 kill)。 |
| D | Disk Sleep (Uninterruptible) | 阻塞 | 深度睡眠。通常是在等待极其重要的磁盘 I/O。不能被信号打断(kill -9都杀不掉),只能等 I/O 完成。 |
| Z | Zombie | 终止 | 僵尸状态。线程退出了,但父进程还没帮它收尸(还没读取它的退出状态)。 |
| T | Stopped | 阻塞 | 暂停。比如被 GDB 调试断点卡住了,或者收到了 SIGSTOP信号。 |
4. 为什么需要了解这些?(开发场景)
- 性能分析: 如果系统很慢,用
top看到大量线程处于 R (Running) 状态,说明 CPU 瓶颈(算力不够)。 如果看到大量线程处于 D (Disk Sleep) 状态,说明 I/O 瓶颈(硬盘坏了或读写太慢)。 - 死锁排查: 如果线程长期处于 S (Sleep) 状态且不处理请求,可能是死锁(在等一个永远不会释放的锁)。
- 高并发编程: 为了提高性能,我们要尽量减少 Running => Blocked 的切换(上下文切换开销大)。 这就是为什么 Nginx、Redis、Node.js 使用 Epoll / 异步非阻塞 I/O:它们让一个线程一直处于 Running 状态处理请求,而不要因为等网络数据而进入 Blocked。
浙公网安备 33010602011771号