并发编程概述:并发与并行的区别、Java并发编程的历史与演进、为什么需要并发编程
各位同学,咱们今天正式开讲并发编程。说实话,这个主题我讲了快十年,每次讲都有新感悟。你想想看,现在随便一个后端服务,QPS 上千是常态,单线程跑?那简直是让 F1 赛车在胡同里开。所以,并发编程不是选修课,是必修课。
一、并发与并行:别再傻傻分不清
很多新手一上来就问:「并发和并行,到底有啥区别?」我当年刚入行时也犯过迷糊。后来在做一个实时风控系统时,才真正搞明白。
并发(Concurrency),说白了就是「看起来同时做」。单核 CPU 也能玩并发,靠的是时间片轮转。你写一段代码,一会儿执行线程 A,一会儿切到线程 B,切换得够快,人就感觉是同时的。嗯,这就像你一边回微信一边写代码——其实是在来回切换。
并行(Parallelism),才是「真正的同时做」。多核 CPU 才能干这事。每个核心跑一个线程,互不干扰。我做过一个图像处理项目,四核机器上把图片分四块并行处理,速度直接翻了近四倍。
核心区别一句话:
- 并发是逻辑上的同时发生(任务交替执行)
- 并行是物理上的同时发生(任务真正同时执行)
举个例子你就懂了。并发就像一个人吃三个馒头,一口一口轮着吃。并行就像三个人同时吃三个馒头。结果都是吃完,但效率完全不同。
我在项目中遇到过一种典型误解:有人觉得只要用了多线程,就一定是并行。其实不然。如果你的机器是单核,再多线程也只是并发,不是并行。你想想看,这就像把十个厨师塞进一个厨房,反而会互相撞到。
我的建议:写代码前,先搞清楚你的部署环境是单核还是多核。这决定了你的并发策略该怎么设计。
二、Java 并发编程的历史与演进
Java 的并发能力,不是一天建成的。我经历了从 JDK 1.4 到现在的 JDK 21,看着它一步步变强。咱们快速过一遍关键节点。
| 版本 | 重要特性 | 我的评价 |
|---|---|---|
| JDK 1.0 | Thread、Runnable、synchronized | 原始但能用,像石器时代 |
| JDK 1.5 | java.util.concurrent 包(JUC) | 里程碑!终于有了高级工具 |
| JDK 1.8 | CompletableFuture、并行流 | 异步编程变得优雅了 |
| JDK 17+ | 虚拟线程(Project Loom) | 轻量级线程,颠覆传统模型 |
JDK 1.0 时代,只有最基础的 Thread 和 synchronized。我刚开始学 Java 时,写个多线程程序就像在走钢丝。一不小心就死锁,或者数据不一致。那时候 debug 全靠加打印语句,想想都心酸。
JDK 1.5 是个分水岭。Doug Lea 大神写的 JUC 包被正式纳入标准库。ReentrantLock、ConcurrentHashMap、线程池……这些工具让并发编程从「手工作坊」进入了「工业化生产」。我记得第一次用线程池时,那种感觉就像从手动挡换成了自动挡。
JDK 1.8 的 CompletableFuture,让异步编程变得像写流水账一样自然。你可以把多个异步任务串起来,还能组合、回调。我曾经用 CompletableFuture 重构过一个老系统的回调地狱,代码量直接减少 60%。
到了 JDK 21 的虚拟线程,我觉得这是 Java 并发史上最革命性的变化。虚拟线程是 JVM 管理的轻量级线程,数量可以开到几十万甚至上百万。以前你不敢为每个请求开一个线程,现在可以了。我在一个高并发网关项目中试过,吞吐量提升了 3 倍以上。
注意:虚拟线程虽好,但不是银弹。如果你的代码里有大量同步块或者 native 方法,虚拟线程的优势会大打折扣。我曾经踩过这个坑,后来老老实实做了压测才上线。
三、为什么需要并发编程?
这个问题,说白了就是一句话:为了榨干硬件的每一滴性能。
咱们从三个角度来拆解。
1. 响应速度:用户等不起
你打开一个 App,如果超过 2 秒没反应,大概率会关掉。现在的用户耐心就这么点。单线程处理请求,就像只有一个收银台的超市,排队排到门外。并发编程就是多开几个收银台,让用户不用等。
我做过一个电商秒杀系统,单线程处理一个请求要 50ms,QPS 最多 20。改成多线程后,QPS 直接飙到 2000。你想想看,这差距有多大。
2. 资源利用率:别让 CPU 闲着
现在的 CPU 动不动就 8 核、16 核。如果你只用一个线程,那剩下的核心都在看热闹。这就像你花大价钱买了辆八缸跑车,却只用一个缸在跑。并发编程就是让所有气缸一起工作。
我记得有个数据处理任务,要处理 100 万条记录。单线程跑了 3 个小时。改成并行流后,20 分钟搞定。老板当时就问我:「你是不是偷偷升级了服务器?」我说没有,只是让所有核心都干活了。
3. 业务复杂性:现实世界就是并发的
你想想看,现实世界本身就是并发的。多个用户同时下单、多个传感器同时上报数据、多个服务同时调用……如果你的程序是单线程的,那它根本没法模拟真实世界。
我曾经做一个物联网平台,上千个设备同时上报数据。如果用单线程处理,数据会堆积如山,延迟越来越高。后来用线程池 + 队列,每个设备的数据独立处理,系统稳如泰山。
总结一下:
- 并发编程让程序跑得更快(充分利用多核)
- 并发编程让程序响应更快(异步处理)
- 并发编程让程序更贴近真实世界(模拟并发场景)
当然,并发编程不是免费的午餐。它带来了线程安全、死锁、活锁、内存一致性等一系列问题。但这些问题,正是咱们这门课要逐一攻克的。
我的经验:初学者不要一上来就追求高并发。先学会用线程池和锁,把单线程的程序改成多线程,然后观察性能变化。慢慢来,比较快。
好,这一章咱们把并发编程的「是什么」和「为什么」讲清楚了。下一章,咱们会深入线程的创建和生命周期,手把手带你写出第一个并发程序。到时候,我会分享一个我早期踩过的坑——线程启动顺序搞反了,导致数据全乱套。敬请期待。
公众号:蓝海资料掘金营,微信deep3321