并发和并行的区别是什么?
结论先行:并发(concurrency)是任务在时间上重叠推进的结构,并行(parallelism)是任务在同一时刻真正同时执行的能力。一句话记法:并发是"看起来同时在跑",并行是"确实同时在跑";并发靠设计,并行靠硬件。
1. 对比表
| 维度 | 并发 | 并行 |
|---|---|---|
| 定义 | 多个任务交替推进,宏观上同时 | 多个任务在同一时刻同时执行 |
| 硬件前提 | 单核即可 | 必须多核 / 多 CPU |
| 关注点 | 任务编排、状态共享、响应性 | 吞吐量、计算加速比 |
| 典型手段 | 多线程、事件循环、协程、异步回调 | 多进程、多线程绑核、并行流、SIMD |
| 瓶颈 | 上下文切换、锁竞争 | 核数、内存带宽、串行部分 |
| 生活类比 | 一个人轮流接三个电话 | 三个人各接一个电话 |
2. 时间线视角
单核并发(交替执行,任一时刻只有一个在跑)
CPU ──[A][B][A][C][B][A]──▶ 时间
↑ 切换点,每次都要保存/恢复上下文
双核并行(同一时刻两个任务都在跑)
CPU0 ──[A][A][A][A]──▶
CPU1 ──[B][B][B][B]──▶
并发解决的是"等待"问题:一个任务在等磁盘、等网络时,把 CPU 让给别的任务,别空转。 并行解决的是"算力"问题:一份活拆给多个核同时算,缩短总耗时。
3. 代码上的区别
// 并发但不并行:单线程事件循环,任务交替推进
ExecutorService single = Executors.newSingleThreadExecutor();
single.submit(() -> handleRequest(1)); // 排队,逐个执行
single.submit(() -> handleRequest(2));
// 并行:CPU 有多核,任务才真正同时跑
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService pool = Executors.newFixedThreadPool(cores);
IntStream.range(0, 8).parallel() // 依赖 ForkJoinPool.commonPool
.forEach(this::heavyCompute); // 核数 < 8 时,必然有任务排队
关键点:线程数大于核数时,多出来的线程只是在并发,不是在并行。newFixedThreadPool(200) 在 8 核机器上并不会让 CPU 密集任务快 25 倍,只会多出 200 份上下文切换开销。
4. 什么时候该用哪个
| 任务特征 | 更该关注 | 理由 |
|---|---|---|
| IO 密集(RPC、DB、文件) | 并发 | 线程大部分时间在等,多开线程能提高利用率 |
| CPU 密集(加解密、压缩、计算) | 并行 | 只有核数能带来加速,线程数≈核数 |
| 需要快速响应用户 | 并发 | 长任务不能阻塞短任务 |
| 批处理大数据 | 并行 | 追求总耗时最短 |
5. 常见追问
追问一:并发一定比串行快吗? 不一定。任务很短、竞争很激烈时,创建线程和上下文切换的开销可能超过任务本身。压测里经常出现"线程数从 8 加到 64,QPS 反而下降",这就是切换与锁竞争吃掉了收益。
追问二:并行加速有上限吗?
有,阿姆达尔定律(Amdahl's Law):若串行部分占比为 s,则无论多少核,加速比上限是 1/s。一个 10% 串行的任务,理论上最多快 10 倍。
追问三:Java 的并行流有什么坑?
并行流共用 ForkJoinPool.commonPool(),默认大小是 核数 - 1。在 Web 请求里用并行流做阻塞 IO,会把公共池占满,拖慢同一 JVM 里所有其他并行流;而且它默认走同一个线程池,无法做隔离。阻塞型任务应该自己建池,不要用 parallelStream。
6. 判断流程
任务能否拆成互不依赖的子任务?
├── 不能 → 只能做并发:用队列 + 工作线程,先解决"等"的问题
└── 能 → 子任务主要在等 IO 还是在算 CPU?
├── 等 IO → 并发:线程数可远大于核数,但要设上限保护下游
└── 算 CPU → 并行:线程数≈核数,并确认串行部分占比不高
追问四:单核机器上写多线程还有意义吗? 有。单核上多线程只能并发,但一个线程阻塞在 IO 上时,其他线程能继续用 CPU,整体吞吐依然提升。只是此时线程数超过 2~3 倍核数就开始净亏。
追问五:分布式系统里说的"高并发"指什么? 指的是大量请求同时到达时的处理能力(QPS、P99),它同时依赖并发(IO 复用、异步)与并行(多核、多实例)。单机讨论并发模型,集群讨论水平扩展,两者不能混为一谈。
小结
并发是结构(怎么把任务拆开、交错推进),并行是执行(同时占用多个核)。面试里最容易被追问的一句是:"IO 密集靠并发提利用率,CPU 密集靠并行缩耗时"——把这句话和线程数≈核数的事实绑在一起,基本就答全了。