进程与线程
问题
进程和线程有什么区别?Java 线程和操作系统线程的关系?
答案
进程 vs 线程
| 维度 | 进程 | 线程 |
|---|---|---|
| 定义 | 资源分配的基本单位 | CPU 调度的基本单位 |
| 资源 | 独立的内存空间 | 共享进程的内存 |
| 开销 | 创建/切换开销大 | 创建/切换开销小 |
| 通信 | IPC(管道、Socket 等) | 共享内存,直接通信 |
| 影响 | 进程崩溃不影响其他进程 | 线程崩溃导致整个进程崩溃 |
| 数量 | 一般较少 | 一个进程可有多个线程 |
线程的状态(操作系统层面)
Java 线程与操作系统线程
Java 线程(HotSpot JVM)是 1:1 映射到操作系统内核线程的。new Thread().start() 最终调用操作系统的 pthread_create()。
Java 21 虚拟线程
Java 21 引入虚拟线程(Virtual Thread),是 M:N 模型——M 个虚拟线程映射到 N 个平台线程。虚拟线程非常轻量(几 KB),可以创建百万级。
// 虚拟线程
Thread.startVirtualThread(() -> {
// 阻塞操作不会占用平台线程
Thread.sleep(1000);
});
详见 Java 新特性。
上下文切换
线程切换时需要保存/恢复 CPU 寄存器、程序计数器、栈指针等状态,这就是上下文切换。切换代价:
- 直接成本:寄存器保存/恢复、内核态切换
- 间接成本:CPU 缓存失效(Cache Miss)
查看 Linux 系统上下文切换次数:vmstat 1 的 cs 列。
常见面试问题
Q1: 什么是协程?Java 支持吗?
答案:
协程是用户态的轻量级线程,调度由用户程序控制,不需要内核参与。
- Go 语言的 goroutine、Kotlin 的 coroutine 都是协程
- Java 21 的虚拟线程(Virtual Thread)本质就是协程实现
协程优势:创建开销极小、无内核态切换、适合高并发 IO 场景。
Q2: 为什么进程切换比线程切换慢?
答案:
进程切换除了保存/恢复线程上下文外,还需要:
- 切换页表(虚拟内存映射)
- 刷新 TLB 缓存(地址翻译缓存)
- 可能导致更严重的 CPU Cache Miss
线程切换只需保存/恢复少量寄存器,因为同进程的线程共享内存空间。
Q3: 多线程一定比单线程快吗?
答案:
不一定。以下场景单线程可能更快:
- CPU 密集型 + 单核:线程切换是纯开销
- 任务很轻:线程创建/切换的开销大于任务本身
- 大量锁竞争:线程频繁阻塞等待
Redis 就是单线程模型(6.0 前),因为瓶颈在网络 IO 而不是 CPU。