进程与线程的区别与联系
进程和线程是操作系统的两个核心概念。408 考试和面试中,二者的对比是必考内容。
基本概念
进程是程序在数据集合上的一次执行过程,是系统进行资源分配的基本单位。
线程是进程内的执行实体,是 CPU 调度的基本单位。一个进程可以包含多个线程,它们共享进程的资源。
mermaid
graph TD
A[操作系统] --> B[进程 1]
A --> C[进程 2]
B --> D[线程 1]
B --> E[线程 2]
B --> F[线程 3]
C --> G[线程 1]
B --- H[共享: 代码段/数据段/堆]
D --- I[私有: 寄存器/栈/PC]五个维度对比
| 维度 | 进程 | 线程 |
|---|---|---|
| 资源 | 独立的地址空间 | 共享进程的地址空间 |
| 调度 | 切换开销大 | 切换开销小 |
| 通信 | 管道、消息队列、共享内存 | 直接读写共享变量 |
| 开销 | 创建/销毁大 | 创建/销毁小 |
| 安全性 | 一个崩溃不影响其他 | 一个崩溃可能导致整个进程崩溃 |
进程间通信(IPC)
由于进程有独立的地址空间,需要操作系统提供 IPC 机制:管道、共享内存、消息队列、信号量、Socket。
线程同步:生产者-消费者
java
class BoundedBuffer<T> {
private final Object[] items;
private int putIdx, takeIdx, count;
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public BoundedBuffer(int capacity) { items = new Object[capacity]; }
@SuppressWarnings("unchecked")
public T take() throws InterruptedException {
lock.lock();
try {
while (count == 0) notEmpty.await();
T item = (T) items[takeIdx];
items[takeIdx] = null;
if (++takeIdx == items.length) takeIdx = 0;
count--;
notFull.signal();
return item;
} finally { lock.unlock(); }
}
public void put(T item) throws InterruptedException {
lock.lock();
try {
while (count == items.length) notFull.await();
items[putIdx] = item;
if (++putIdx == items.length) putIdx = 0;
count++;
notEmpty.signal();
} finally { lock.unlock(); }
}
}死锁的四个必要条件
- 互斥:资源同一时刻只能被一个线程使用
- 占有并等待:持有资源的同时等待另一个
- 不可剥夺:资源不能被强行夺走
- 循环等待:多个线程形成环形等待链
避免死锁
- 固定锁顺序:所有线程按相同顺序获取锁
- 设置超时:lock.tryLock(timeout) 避免无限等待
- 死锁检测:定时检查并主动中断
上下文切换开销
| 切换类型 | 耗时级别 | 原因 |
|---|---|---|
| 进程切换 | 微秒级 | 切换地址空间、页表、TLB 刷新 |
| 线程切换 | 亚微秒级 | 只切换寄存器、栈指针 |
小结
进程是资源分配的单位,线程是 CPU 调度的单位。进程隔离性好但通信开销大,线程轻量但需仔细处理同步。