深入理解线程模型
线程作为操作系统调度的基本执行单元,是实现高吞吐、低延迟系统的基础。
一、进程与线程的体系结构对比
核心概念:
- 进程(Process):操作系统资源分配的基本单位,拥有独立的虚拟地址空间、文件描述符表、环境变量等。
- 线程(Thread):CPU 调度与分派的基本单位,属于进程,共享进程的地址空间和资源,但拥有独立的栈空间、程序计数器、寄存器集合。
关键区别:
资源开销:线程创建/切换开销远小于进程(无需切换地址空间)。
通信效率:线程间共享内存,通信无需 IPC(进程间通信)机制。
健壮性:一个线程崩溃可能导致整个进程终止;进程间相互隔离。
二、线程生命周期与状态转换
状态详解:
- NEW:线程实例化后,尚未调用
start()
。 - RUNNABLE:已调用
start()
,等待或正在 CPU 上执行(包含操作系统层面的 Running 和 Ready)。 - BLOCKED:等待进入
synchronized
代码块(等待获取监视器锁)。 - WAITING:调用
Object.wait()
,Thread.join()
,LockSupport.park()
后无限期等待。 - TIMED_WAITING:调用
Thread.sleep()
,Object.wait(timeout)
,Thread.join(timeout)
等带超时的方法。 - TERMINATED:线程执行完毕或因未捕获异常终止。
三、并发和并行
核心定义:
并发:多个任务在同一时间段内交替执行(宏观上“同时”,微观上分时复用 CPU)。适用于 I/O 密集型任务。
并行:多个任务在同一时刻真正同时执行(需多核/多处理器支持)。适用于 CPU 密集型任务。
并发是编程模型,解决任务调度问题;并行是硬件能力,解决计算加速问题。
四、线程安全与同步机制:竞态条件与内存可见性
1. 竞态条件
当多个线程非原子地访问共享资源,且最终结果依赖于线程调度顺序时,即发生竞态条件。
public class Counter {private int count = 0;public void increment() {count++; // 非原子操作:读取 → 修改 → 写入}public int getCount() { return count; }
}
2. 同步原语
互斥锁(Mutex / synchronized):确保同一时刻只有一个线程访问临界区。
信号量(Semaphore):控制同时访问资源的线程数量。
条件变量(Condition Variable / wait/notify):线程间通信,实现等待/通知机制。
原子变量(Atomic Variables):CAS(Compare-And-Swap)操作实现无锁线程安全。
3. 内存可见性
由于 CPU 缓存和编译器优化,一个线程对共享变量的修改,可能不能立即被其他线程看到。volatile
关键字或同步块可保证可见性。
五、线程调度与上下文切换开销
调度策略:操作系统(如 Linux CFS)根据优先级、时间片等决定哪个线程获得 CPU。
上下文切换(Context Switch):
保存当前线程状态(寄存器、PC、栈指针等),恢复另一线程状态。开销包括:
直接开销:CPU 缓存失效、TLB 刷新。
间接开销:调度器计算、锁竞争加剧。
优化建议:
避免创建过多线程(使用线程池)。
减少锁竞争(无锁数据结构、分段锁)。
绑定 CPU 核心(减少缓存失效)。