并发和并行的区别是什么?


一则或许对你有用的小广告

欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书

  • 《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于 Spring AI + Spring Boot 3.x + JDK 21...查看介绍

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...查看介绍;演示链接:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/

  • 新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍

截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 概念清晰度:面试官不仅仅是想知道你能不能背出定义,更是想看你能不能用生活化的例子把这两个概念讲清楚。能讲清楚,说明你真理解了;只会背定义,说明只是 "看过" 而非 "懂了"。

  2. 底层机制理解:考察你是否知道并发依赖 CPU 时间片轮转(上下文切换),并行依赖多核 CPU 同时执行。这涉及到操作系统层面的调度机制,能区分出你的知识深度。

  3. Java 关联:能不能把这两个概念和 Java 的多线程、ForkJoinPoolparallelStream 等联系起来,说明你有实战思维,不是纯背概念。

核心答案

一句话区分:并发是 "同时处理" 多件事,并行是 "同时做" 多件事。

对比维度 并发(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 线程模型天然支持并发,而 ForkJoinPoolparallelStream 则是 Java 在并行计算上的支持。

面试高频追问

  1. 什么是上下文切换?有什么影响?
    • 线程切换时需要保存和恢复执行状态,这个过程有 CPU 开销。线程数过多时,上下文切换频繁,反而会降低性能。
  2. Java 中怎么利用多核并行?
    • ForkJoinPoolparallelStream()CompletableFuture 配合自定义线程池,都可以充分利用多核。
  3. 单核 CPU 有并行的意义吗?
    • 单核 CPU 无法真正并行执行多个线程(超线程除外),但并发仍然有意义——比如一个线程等 IO 时,另一个线程可以用 CPU。

记忆口诀

并发是 "同时应对",并行是 "同时干活"。一个是调度能力,一个是执行能力。

总结

面试答这道题,三层递进:先说清楚定义(逻辑同时 vs 物理同时),再从 CPU 调度角度解释底层机制(时间片轮转 vs 多核执行),最后点明它们不是对立概念——并发是程序结构,并行是执行方式。能把这三层讲明白,面试官就知道你是真理解了。