并发和并行的区别是什么?
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
面试考察点
-
概念清晰度:面试官不仅仅是想知道你能不能背出定义,更是想看你能不能用生活化的例子把这两个概念讲清楚。能讲清楚,说明你真理解了;只会背定义,说明只是 "看过" 而非 "懂了"。
-
底层机制理解:考察你是否知道并发依赖 CPU 时间片轮转(上下文切换),并行依赖多核 CPU 同时执行。这涉及到操作系统层面的调度机制,能区分出你的知识深度。
-
Java 关联:能不能把这两个概念和 Java 的多线程、
ForkJoinPool、parallelStream等联系起来,说明你有实战思维,不是纯背概念。
核心答案
一句话区分:并发是 "同时处理" 多件事,并行是 "同时做" 多件事。
| 对比维度 | 并发(Concurrency) | 并行(Parallelism) |
|---|---|---|
| 核心定义 | 多个任务在 逻辑上 同时推进 | 多个任务在 物理上 同时执行 |
| 硬件要求 | 单核 CPU 即可实现 | 需要 多核 CPU |
| 执行方式 | 交替执行(时间片轮转) | 真正同时执行 |
| 本质 | 任务调度能力 | 任务执行能力 |
| 类比 | 一个人交替打电话和发邮件 | 两个人分别打电话和发邮件 |
深度解析
一、生活类比——最直观的理解
-
并发就像一个厨师同时准备两道菜:先切会儿西红柿,放下刀去搅鸡蛋,搅完鸡蛋又回来切西红柿。看起来两道菜在 "同时" 准备,但其实厨师是在两件事之间来回切换。关键点是:只有一个 CPU(厨师),靠频繁切换来营造 "同时" 的错觉。
-
并行就像两个厨师各做各的菜:厨师 A 做西红柿炒蛋,厨师 B 做宫保鸡丁,两个人真正同时在工作,互不干扰。关键点是:有多个 CPU(厨师),每个核心独立执行一个任务。
二、从 CPU 角度理解
并发的底层是上下文切换:
-
时间片轮转:操作系统把 CPU 时间切成很小的时间片(通常几毫秒),每个线程分配一个时间片,轮流执行。因为切换速度极快,人感觉像是 "同时" 在运行。
-
上下文切换有开销:每次切换需要保存当前线程的执行状态(寄存器、程序计数器等),然后加载下一个线程的状态。频繁的上下文切换会影响性能,这也是为什么线程数不是越多越好的原因之一。
并行的底层是多核同时执行:
- 多核 CPU 上,每个核心可以独立执行一个线程,是真正的物理并行,不需要来回切换。
三、它们的关系——不是对立,而是协作
这点很多人搞混:并发和并行 不是互斥的概念。
Rob Pike(Go 语言之父)有句经典的话:
"Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once."
并发是关于 "同时处理" 很多事情。并行是关于 "同时执行" 很多事情。
换句话说:
- 并发是一种 程序结构 设计——你把程序设计成多个独立任务可以交错推进
- 并行是一种 执行方式——多个任务在物理上同时跑
一个并发程序在单核 CPU 上以交替方式运行,在多核 CPU 上则以并行方式运行。并发是设计,并行是执行。
四、Java 中的体现
// 并发:多线程在单核上交替执行,也可以在多核上并行执行
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> task1()); // 线程 A
executor.submit(() -> task2()); // 线程 B
executor.submit(() -> task3()); // 线程 C
// 并行:明确利用多核同时计算
list.parallelStream().forEach(item -> process(item));
// ForkJoinPool:专为并行计算设计
ForkJoinPool pool = new ForkJoinPool(4);
pool.invoke(new RecursiveTask<Void>() {
@Override
protected Void compute() {
// 任务拆分,多核并行执行
}
});
Java 线程模型天然支持并发,而 ForkJoinPool、parallelStream 则是 Java 在并行计算上的支持。
面试高频追问
- 什么是上下文切换?有什么影响?
- 线程切换时需要保存和恢复执行状态,这个过程有 CPU 开销。线程数过多时,上下文切换频繁,反而会降低性能。
- Java 中怎么利用多核并行?
ForkJoinPool、parallelStream()、CompletableFuture配合自定义线程池,都可以充分利用多核。
- 单核 CPU 有并行的意义吗?
- 单核 CPU 无法真正并行执行多个线程(超线程除外),但并发仍然有意义——比如一个线程等 IO 时,另一个线程可以用 CPU。
记忆口诀
并发是 "同时应对",并行是 "同时干活"。一个是调度能力,一个是执行能力。
总结
面试答这道题,三层递进:先说清楚定义(逻辑同时 vs 物理同时),再从 CPU 调度角度解释底层机制(时间片轮转 vs 多核执行),最后点明它们不是对立概念——并发是程序结构,并行是执行方式。能把这三层讲明白,面试官就知道你是真理解了。