1. 概述
在操作系统中,我们经常需要处理多个任务的执行问题。这时候就离不开两个关键技术:并发(Concurrency) 和 并行(Parallelism)。
虽然这两个术语听起来很像,但它们的实现机制和适用场景完全不同。这篇文章会从底层原理到实际应用,带你彻底搞懂并发与并行的区别与联系。
2. 核心定义
在深入探讨并发与并行之前,先明确几个关键概念:
- ✅ 多处理(Multiprocessing):在一个计算机系统中使用多个CPU。
- ✅ 多线程(Multithreading):一个进程可以包含多个线程,这些线程可以并发执行。
- ✅ 分布式计算(Distributed Computing):多个计算机系统作为一个整体运行,可以通过局域网或广域网连接。
- ✅ 多核处理器(Multicore Processor):一个芯片上集成多个核心处理单元。
- ✅ 流水线(Pipelining):多个指令在执行时可以重叠执行。
这些概念是理解并发和并行的基础,务必掌握。
3. 并发(Concurrency)
并发是指多个任务在重叠的时间段内执行。任务之间可以交替执行,但不一定真正同时运行。
比如,你一边听歌一边打字,看起来是同时进行的,其实操作系统是在多个任务之间快速切换,给人“同时进行”的错觉。
3.1 并发是如何工作的?
并发的核心目标是最大化CPU利用率。当一个线程在等待IO、数据库操作或外部程序时,另一个线程可以继续使用CPU。
操作系统通过时间片轮转(Round Robin)和上下文切换来实现并发。如下图所示:
⚠️ 注意:并发执行时,多个线程共享资源,尤其是写操作时容易引发数据竞争(Race Condition),必须通过锁机制(如synchronized、ReentrantLock)来保证线程安全。
4. 并行(Parallelism)
并行是指多个任务在同一时刻真正同时执行。它依赖于多核处理器或分布式系统。
比如,一个多核CPU可以真正同时执行多个线程,或者一个Hadoop集群可以同时处理多个节点上的数据。
4.1 并行是如何工作的?
并行通常出现在以下几种场景:
- ✅ 多核CPU上的多线程任务
- ✅ 分布式系统中的多个节点协同处理
- ✅ 流水线式指令执行(如GPU计算)
如下图所示,多个任务在不同核心上并行执行:
并行的优势在于提升计算效率,适用于计算密集型任务,如图像处理、大数据分析等。
5. 并发 vs 并行:关键区别
对比项 | 并发(Concurrency) | 并行(Parallelism) |
---|---|---|
执行方式 | 交替执行,任务切换 | 真正同时执行 |
资源要求 | 单核或多核均可 | 通常需要多核或分布式系统 |
适用场景 | IO密集型任务(如Web服务器) | CPU密集型任务(如图像处理) |
实现难度 | 中等,需处理线程安全 | 较高,需处理数据同步与分布 |
典型例子 | Java线程池、Netty异步IO | Hadoop、Spark、GPU并行计算 |
5.1 举个例子
下图展示了并发和并行在两个核心上的执行方式:
左边是并发:两个核心交替执行两个任务;
右边是并行:两个核心同时执行两个任务。
5.2 常见陷阱
并发和并行虽然强大,但如果不小心使用,可能会带来严重问题:
- ❌ 死锁(Deadlock):多个线程互相等待对方释放资源,导致程序卡死。
- ❌ 竞态条件(Race Condition):多个线程同时修改共享变量,导致数据不一致。
- ❌ 资源饥饿(Starvation):某些线程始终得不到CPU时间。
- ❌ 内存泄漏(Memory Leak):未正确释放线程或对象引用,导致内存占用过高。
⚠️ 踩坑提醒:在并发编程中,不要盲目使用Thread.sleep()
来解决同步问题,这是“掩耳盗铃”的做法,真正应该用的是锁、原子操作或并发工具类。
6. 支持并发与并行的编程语言和框架
Java 作为一门成熟的并发编程语言,内置了丰富的并发工具类(如java.util.concurrent
包),同时也支持并行计算(如Fork/Join框架)。
下面是一些支持并发与并行的主流语言和框架:
✅ 共享内存语言
- Java(推荐)
- C(配合POSIX线程库)
- C++
✅ 分布式内存语言
- Go(推荐用于并发和分布式)
- Rust(推荐用于系统级并发)
- MPI(C/C++)
✅ 并行函数式语言
- Scala(Akka框架)
- LISP(早期并行语言)
✅ 大数据处理框架
- Hadoop
- Spark(推荐)
✅ 并发工具库
- Java的
ExecutorService
、ForkJoinPool
- Go的
goroutine
和channel
- Python的
multiprocessing
和asyncio
✅ 建议:掌握并发/并行编程,不要只看语言本身,更重要的是理解底层机制,比如线程调度、锁机制、内存模型等。
7. 总结
并发和并行是现代系统设计中不可或缺的两个概念:
- ✅ 并发:多个任务交替执行,提升响应性,适用于IO密集型任务。
- ✅ 并行:多个任务同时执行,提升吞吐量,适用于CPU密集型任务。
- ⚠️ 并发 ≠ 并行,它们是两个不同的概念,但可以结合使用。
- ❌ 并发编程容易出错,要特别注意线程安全、死锁、竞态等问题。
- ✅ Java程序员应该熟练掌握并发编程,了解线程池、锁机制、volatile关键字、CAS算法等核心知识点。
掌握了并发与并行,才能写出高性能、高可用的系统。否则,再多的CPU资源也发挥不出来。