🚪 并发入门篇(零基础版)
线程是什么 · 为什么并发会出问题 · 锁是什么 · 线程安全——全部大白话
1. 线程是什么?为什么需要多个线程?
👨🍳 类比——厨房:
程序 = 一家餐厅;进程 = 一个厨房;线程 = 厨房里的厨师
一个厨师(单线程):只能一道菜一道菜做——菜 A 在等水烧开时,厨师干等着,浪费!
多个厨师(多线程):菜 A 等水烧开时,厨师 B 可以切菜——同时做多件事,效率高。
程序 = 一家餐厅;进程 = 一个厨房;线程 = 厨房里的厨师
一个厨师(单线程):只能一道菜一道菜做——菜 A 在等水烧开时,厨师干等着,浪费!
多个厨师(多线程):菜 A 等水烧开时,厨师 B 可以切菜——同时做多件事,效率高。
线程:程序里同时执行的多条"任务线"。Java 里 new Thread(...).start() 就能开一条新线程。
为什么要用多线程:
- 网站一次来 1000 个用户请求——每个请求一个线程处理,否则排队等到天荒地老
- 一个请求里要查数据库 + 调外部接口——并行做,响应更快
感受线程:同时打印
new Thread(() -> {
for (int i = 1; i <= 5; i++)
System.out.println("线程1: " + i);
}).start();
for (int i = 1; i <= 5; i++)
System.out.println("主线程: " + i);
// 输出顺序不固定!两个线程交替执行(由 CPU 调度)
// 可能:主线程:1 线程1:1 主线程:2 线程1:2 ... 交替出现
🎯 记住
- 线程 = 并行的"任务线";多线程 = 多个厨师同时干活
- 多线程的代价:抢共享资源会出问题(下一节)
- 线程不是越多越好(创建/切换有成本)——线程池页详解
2. 为什么多线程会出问题?(并发三大问题)
💰 类比——抢红包计数器:两个线程同时执行 count++:
"读 count → 加 1 → 写回"是三步。两个厨师同时做这三步就会"撞车":
厨师 A 读到 100,厨师 B 也读到 100,都加 1 写回 → 结果 101,丢了一次计数。
"读 count → 加 1 → 写回"是三步。两个厨师同时做这三步就会"撞车":
厨师 A 读到 100,厨师 B 也读到 100,都加 1 写回 → 结果 101,丢了一次计数。
并发三大问题(面试必背):
- 原子性:操作被拆成多步,中间被插队(上面的 count++ 例子)
- 可见性:A 线程改了值,B 线程看不到(CPU 缓存没刷新)
- 有序性:代码可能被"重排"执行(编译器/CPU 优化,多线程下顺序不可靠)
亲手复现:多线程计数错误
static int count = 0;
// 开 2 个线程,各加 10000 次
for (int t = 0; t < 2; t++) {
new Thread(() -> {
for (int i = 0; i < 10000; i++) count++;
}).start();
}
// 最终 count 往往不是 20000,而是 199xx——因为撞车丢了更新
// 这就是"线程不安全":多线程下结果不可预测
🎯 记住
- 并发问题 = 多个线程"同时"操作共享数据 → 结果不可预测
- 三大问题:原子性(被插队)、可见性(看不到)、有序性(被重排)
- 解决思路就是后面整个模块:锁(synchronized)、volatile、原子类
3. 锁(synchronized)是什么?
🚻 类比——公共厕所:多线程抢共享资源就像抢厕所:
synchronized(锁) = 门上锁:一个人进去(拿到锁),其他人只能在门外等(阻塞),里面的人出来(释放锁),下一个才能进。
锁保证:同一时刻只有一个线程执行被锁的代码 → 不会撞车。
synchronized(锁) = 门上锁:一个人进去(拿到锁),其他人只能在门外等(阻塞),里面的人出来(释放锁),下一个才能进。
锁保证:同一时刻只有一个线程执行被锁的代码 → 不会撞车。
用锁修复计数问题
static int count = 0;
static final Object LOCK = new Object();
new Thread(() -> {
for (int i = 0; i < 10000; i++) {
synchronized (LOCK) { // 拿锁 → 执行 → 释放
count++;
}
}
}).start();
// 现在结果稳定是 10000(每个线程自己的 10000 次不互相干扰)
// 代价:加锁会慢一点(排队),所以锁的"范围"越小越好
🎯 记住
- synchronized = 给代码块"上锁",同一时刻只放一个线程进来
- 锁要锁"共享数据的操作";锁多了慢,锁少了错——平衡是艺术
- Java 的锁体系:synchronized(内置锁)→ Lock(ReentrantLock,更灵活)——JUC 页详解
4. 并发学习路径
- 本篇:理解线程、并发问题、锁的直觉 ✅
- 线程基础:生命周期、创建方式、wait/notify
- synchronized & volatile:锁升级原理、可见性
- AQS 与 JUC:ReentrantLock、CountDownLatch、原子类、并发容器
- 线程池:生产必用,参数与坑
🎯 常见误区
- 误区 1:"多线程一定快"——线程创建/切换有开销,简单任务单线程更快
- 误区 2:"加了 volatile 就安全了"——volatile 只管可见性,不管原子性(count++ 照样错)
- 误区 3:"synchronized 很慢"——现代 JVM 有锁升级优化(偏向锁→轻量级→重量级),大多数场景够快。注意 JDK 15 起偏向锁已默认关闭(JEP 374)