1. 为什么要学并发编程?
先抛一个现实问题:
- 双 11 秒杀场景,数百万用户同时点下"立即购买";
- 微信消息推送,数亿用户同时上线;
- 银行支付交易,必须保证转账数据准确、资金不丢不乱。
如果系统还是"单线程串行执行",会怎样?
👉 结果只能是 卡死、响应慢、资源利用率低。
现代软件开发几乎离不开并发编程:
- 性能:充分利用多核 CPU,让程序并行执行。
- 响应性:避免主线程阻塞,提升用户体验。
- 高可用:在大规模请求下保持系统稳定。
因此,并发编程不是"锦上添花",而是 大型系统必备能力。
2. 单线程编程的局限
早期的计算机和程序都是 单线程模型:
java
public class SingleThreadDemo {
public static void main(String[] args) {
task1();
task2();
task3();
}
}
所有任务 顺序执行,CPU 一次只能干一件事。
- 如果某个任务耗时过长,后续任务就只能"干等"。
- 在 I/O 密集型任务(如读文件、网络请求)中,这种等待更明显。
👉 单线程模型非常简单,没有线程安全问题,但它完全无法满足现代互联网场景。
3. 多进程的引入
后来,操作系统支持了 多进程模型。
- 每个进程拥有独立的内存空间;
- 多个进程可以并行执行;
- 进程之间隔离性强,不会直接影响彼此。
但问题也来了:
- 开销大:进程切换需要保存上下文,频繁切换很耗性能。
- 通信复杂:不同进程间共享数据困难,只能靠 IPC(管道、Socket、共享内存等)。
👉 多进程适合安全性要求高的场景,比如浏览器(Chrome 的每个标签页就是一个独立进程)。
4. 多线程的出现
为了在性能与通信之间取得平衡,多线程模型应运而生。
- 一个进程中可以有多个线程;
- 线程共享同一块内存空间,通信更高效;
- 线程创建与切换比进程轻量。
例如 Java 创建线程的几种方式:
java
// 方式一:继承 Thread
class MyThread extends Thread {
public void run() {
System.out.println("Hello from Thread!");
}
}
new MyThread().start();
// 方式二:实现 Runnable
new Thread(() -> System.out.println("Hello from Runnable!")).start();
多线程大大提升了系统的 并发能力。
- CPU 密集型任务:多个线程可以并行计算,利用多核 CPU。
- I/O 密集型任务:当一个线程阻塞时,其他线程还能继续工作。
5. 并发编程的挑战
但"福兮祸所依"。
多线程带来了强大性能的同时,也引入了新的问题:
- 线程安全:多个线程同时修改共享变量,会导致数据错乱。
- 死锁:线程之间相互等待,导致程序卡死。
- 上下文切换:线程太多,切换成本反而拖慢系统。
- 调试困难:并发 Bug 不易复现,排查成本高。
👉 所以,学习并发编程不仅仅是"会用线程池",而是要理解底层原理,掌握正确姿势。
6. 总结
- 单线程:实现简单,但性能不足。
- 多进程:隔离性强,但开销大、通信复杂。
- 多线程:高效且灵活,但容易引发线程安全问题。
在如今 多核 CPU 普及 、高并发业务常态化 的背景下,Java 并发编程已经成为后端开发的 必修课。
学会并发编程,就像掌握了一把"双刃剑",用得好能让系统性能飞跃,用不好则可能让系统崩盘。