为什么需要并发编程 概述一句话并发编程是为了解决单线程处理不过来的问题——让程序能同时做多件事提升性能和响应速度。从一个真实场景说起想象一下你开了一家网店平时一天卖 100 单用一个客服处理订单绰绰有余。突然双十一来了同时涌入1000 个订单。如果你还坚持一个客服处理完一个再处理下一个会发生什么第一个用户等了 10 分钟才拿到确认第十个用户等了 100 分钟直接退款走人第一百个用户等到页面超时再也不来了问题的本质单线程单客服的处理能力是有限的当请求量超过处理速度时系统就会崩溃。解决方案多雇几个客服多线程同时处理多个订单。这就是并发编程的动机。 生活类比厨房做饭单线程一个人做饭想象你一个人在厨房做饭流程是这样的总耗时5 3 5 1 14 分钟。在这 14 分钟里你一直在忙但效率很低——洗菜时灶台是空的切菜时水龙头是空的。多线程厨房多人协作现在雇了三个人你负责切菜小王负责洗菜小李负责炒菜。三个人分工合作总耗时可能5 分钟就能上好几道菜。因为三个人同时在做不同的事。但是多人协作也会出问题假设你和小王都想用同一个锅你正在炒菜小王也想炒菜两个人抢一个锅结果菜洒了锅也坏了这就是并发编程中的资源争抢问题。解决方法是什么加锁——这个锅我在用你等一下。这就是后续章节要讲的synchronized和Lock。 单线程的 3 大瓶颈在现代计算机中单线程程序有三个致命问题瓶颈问题描述生活类比影响CPU 利用率低等待 I/O网络/磁盘/数据库时 CPU 空转厨师等外卖食材站在厨房发呆资源浪费处理速度慢响应性差一个任务卡住整个程序卡住厨师等食材所有客人都饿着用户体验差系统不可用吞吐量低无法利用多核 CPU厨房有 4 个灶台只用 1 个性能天花板低代码演示单线程的低效public class SingleThreadDemo { public static void main(String[] args) { long start System.currentTimeMillis(); // 模拟 3 个耗时任务每个 1 秒 task(任务1, 1000); task(任务2, 1000); task(任务3, 1000); long end System.currentTimeMillis(); System.out.println(总耗时: (end - start) ms); } static void task(String name, long sleepTime) { try { System.out.println(开始执行: name); Thread.sleep(sleepTime); // 模拟 I/O 等待 System.out.println(完成: name); } catch (InterruptedException e) { e.printStackTrace(); } } }预期输出开始执行: 任务1 完成: 任务1 开始执行: 任务2 完成: 任务2 开始执行: 任务3 完成: 任务3 总耗时: 3000ms问题3 个任务串行执行总耗时 任务1 任务2 任务3 3 秒。在等待任务1完成时CPU 是空闲的但没人用它。 并发解决了什么问题并发编程通过同时处理多个任务来解决单线程的瓶颈解决方案原理适用场景效果多核并行计算多个 CPU 核心同时执行不同任务CPU 密集型计算、加密性能提升 N 倍N核心数异步处理主线程不等待任务完成后回调I/O 密集型网络、数据库响应时间大幅降低资源利用等待时让出 CPU 给其他任务混合场景系统整体吞吐量提升代码演示多线程并行处理public class MultiThreadDemo { public static void main(String[] args) { long start System.currentTimeMillis(); // 创建 3 个线程同时执行 Thread t1 new Thread(() - task(任务1, 1000)); Thread t2 new Thread(() - task(任务2, 1000)); Thread t3 new Thread(() - task(任务3, 1000)); t1.start(); t2.start(); t3.start(); try { t1.join(); // 等待 t1 完成 t2.join(); // 等待 t2 完成 t3.join(); // 等待 t3 完成 } catch (InterruptedException e) { e.printStackTrace(); } long end System.currentTimeMillis(); System.out.println(总耗时: (end - start) ms); } static void task(String name, long sleepTime) { try { System.out.println(开始执行: name); Thread.sleep(sleepTime); System.out.println(完成: name); } catch (InterruptedException e) { e.printStackTrace(); } } }预期输出开始执行: 任务1 开始执行: 任务2 开始执行: 任务3 完成: 任务1 完成: 任务2 完成: 任务3 总耗时: 1000ms对比多线程总耗时1 秒是单线程的1/3。因为 3 个任务同时执行。 并发的演进Java 并发编程经历了 20 多年的演进版本新增特性解决的问题JDK 1.0Thread、synchronized基础多线程支持JDK 5.0java.util.concurrent包高效并发工具、线程池、并发集合JDK 8CompletableFuture、Lambda异步编程简化JDK 21虚拟线程、结构化并发轻量级线程、更好的并发控制 可跑代码3 个完整示例示例 1单线程处理慢public class SlowServer { public static void main(String[] args) { System.out.println( 单线程处理 3 个请求 ); long start System.currentTimeMillis(); // 模拟处理 3 个请求 handleRequest(用户A查询订单); handleRequest(用户B提交支付); handleRequest(用户C下载发票); long end System.currentTimeMillis(); System.out.println(总耗时: (end - start) ms); System.out.println(用户平均等待: (end - start) / 3 ms); } static void handleRequest(String request) { try { System.out.println(处理: request); Thread.sleep(1000); // 模拟网络 I/O System.out.println(完成: request); } catch (InterruptedException e) { e.printStackTrace(); } } }预期输出 单线程处理 3 个请求 处理: 用户A查询订单 完成: 用户A查询订单 处理: 用户B提交支付 完成: 用户B提交支付 处理: 用户C下载发票 完成: 用户C下载发票 总耗时: 3000ms 用户平均等待: 1000ms示例 2多线程并行处理快public class FastServer { public static void main(String[] args) { System.out.println( 多线程处理 3 个请求 ); long start System.currentTimeMillis(); // 创建 3 个线程并行处理 Thread t1 new Thread(() - handleRequest(用户A查询订单)); Thread t2 new Thread(() - handleRequest(用户B提交支付)); Thread t3 new Thread(() - handleRequest(用户C下载发票)); t1.start(); t2.start(); t3.start(); // 等待所有线程完成 try { t1.join(); t2.join(); t3.join(); } catch (InterruptedException e) { e.printStackTrace(); } long end System.currentTimeMillis(); System.out.println(总耗时: (end - start) ms); System.out.println(用户平均等待: (end - start) / 3 ms); } static void handleRequest(String request) { try { System.out.println(处理: request); Thread.sleep(1000); System.out.println(完成: request); } catch (InterruptedException e) { e.printStackTrace(); } } }预期输出 多线程处理 3 个请求 处理: 用户A查询订单 处理: 用户B提交支付 处理: 用户C下载发票 完成: 用户A查询订单 完成: 用户B提交支付 完成: 用户C下载发票 总耗时: 1000ms 用户平均等待: 333ms示例 3线程不安全的反例引出后续模块public class UnsafeCounter { private static int count 0; public static void main(String[] args) throws InterruptedException { System.out.println( 线程不安全示例 ); // 两个线程各加 10000 次 Thread t1 new Thread(() - { for (int i 0; i 10000; i) { count; // 这行不是原子操作 } }); Thread t2 new Thread(() - { for (int i 0; i 10000; i) { count; // 这行不是原子操作 } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(期望值: 20000); System.out.println(实际值: count); System.out.println(差值: (20000 - count)); // 为什么不是 20000 // 因为 count 不是原子操作多线程下会丢失更新 // 后续章节会详细讲解 CAS 和 synchronized } }预期输出每次运行可能不同 线程不安全示例 期望值: 20000 实际值: 15234 差值: 4766问题分析count看起来是一行代码但实际上分三步读取 count 的值加 1写回 count当两个线程同时执行时可能读到相同的值导致加 1 后覆盖对方的结果。这就是竞态条件。⚠️ 并发的代价预告多人协作虽然快但也会出问题问题描述生活类比后续章节竞态条件多个线程同时修改共享数据结果不可预期两个人同时改同一个文档02_原理与实现/04_锁体系总览死锁两个线程互相等待对方释放锁卡死两个人互相让路结果都卡在门口03_问题与应对/03_并发问题排查实战可见性问题一个线程修改了变量另一个线程看不到一个人在黑板上写字另一个人没看到02_原理与实现/03_并发内存模型线程安全多线程下数据结构可能损坏多人同时写账本账本可能乱套04_应用/07_并发集合详解怎么解决后面的篇目会一一拆解锁机制synchronized、ReentrantLock原子类AtomicInteger、CASvolatile 关键字并发集合ConcurrentHashMap线程池和同步器// 竞态条件示例两个人同时改同一个文档 public class RaceConditionExample { private static int balance 100; public static void main(String[] args) throws InterruptedException { // 模拟两个线程同时扣款 Thread t1 new Thread(() - { int temp balance; try { Thread.sleep(10); } catch (Exception e) {} balance temp - 50; System.out.println(用户A扣款后余额: balance); }); Thread t2 new Thread(() - { int temp balance; try { Thread.sleep(10); } catch (Exception e) {} balance temp - 50; System.out.println(用户B扣款后余额: balance); }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(最终余额: balance); System.out.println(期望余额: 0, 实际: balance (应该为0)); } } 学习路径作为专栏入口这里告诉你接下来的模块怎么走学习阶段核心内容你会收获入门线程基础、内存模型、volatile、synchronized理解并发的基本概念能写简单的线程安全代码进阶锁机制、AQS、线程池、CompletableFuture看懂 JUC 源码会设计并发方案实战死锁排查、设计模式、虚拟线程能排查线上问题能设计并发架构 面试要点为什么用多线程提升性能多核 CPU 并行计算提升响应I/O 等待时 CPU 做其他事提升吞吐同时处理更多请求并发 vs 并行区别并发多个任务交替执行单核也能做到并行多个任务同时执行需要多核并发的代价竞态条件数据不一致死锁线程互相等待可见性修改不可见复杂度代码更难写、更难测单线程 vs 多线程性能对比CPU 密集型多线程 ≈ 单线程甚至更慢因为线程切换开销I/O 密集型多线程 单线程等待时做其他事如何避免线程安全问题加锁synchronized、ReentrantLock原子类AtomicInteger、CASvolatile保证可见性并发集合ConcurrentHashMap 总结要点记住这一句动机单线程处理不过来需要多线程并行类比一个人做饭 vs 厨房多人协作瓶颈CPU 空转、响应差、吞吐低解决多核并行、异步处理、资源复用代价竞态、死锁、可见性问题演进Thread → JUC → 虚拟线程下一步学线程基础理解线程是什么一句话总结并发编程是为了解决慢的问题但引入了乱的问题——后面的章节会教你如何又快又稳。

本月热点