📄 一句话概括
五节一条线:认识工厂 → 请工人 → 看一生 → 发工具 → 上锁。多线程的一切都是「并发的收益」和「共享的代价」做交换——想快就并行,并行就要共享,共享就要上锁。
一、五节串线
| 节 | 主题 | 一句话收口 |
|---|---|---|
| 12.1 | 概念 | 进程=工厂(独立内存),线程=工人(共享资源、真正干活);并发=交替的错觉,并行=多核真同时 |
| 12.2 | 创建 | 继承 Thread 占继承名额,实现 Runnable 任务工人分离(Lambda 老主场);start 真上工,run 假上工 |
| 12.3 | 生命周期 | 六状态 + 三歇脚点;RUNNABLE ≠ 正在跑;TERMINATED 不能复生(start 只能一次的谜底) |
| 12.4 | 方法 | static 作用于当前线程,实例方法作用于这条线程;join=等收工再走;GC=守护线程 |
| 12.5 | 同步 | count++ 三步会插队丢更新;锁=排队;多线程必须用同一把锁;11.4 抢桌子账清算 |
二、概念速查 12 条
- 进程:正在运行的程序,独立内存围墙;任务管理器一行一个
- 线程:进程里真正干活的单位,共享进程资源
- 并发:单核交替(宏观同时微观轮流);并行:多核同时
- 抢占式调度:谁抢到 CPU 谁跑,完全随机
- main 主线程:
Thread.currentThread().getName()亲眼看 - Thread / Runnable:自带头脑的工人 vs 剥出来的任务单(函数式接口)
- start vs run:通知 JVM 开新线程 vs 主线程普通方法调用
- 六状态:NEW → RUNNABLE →(TIMED_WAITING / WAITING / BLOCKED)→ TERMINATED
- sleep / join:定时歇(静态、睡当前)vs 等收工(实例、谁调谁等)
- 守护线程:用户线程全退它跟着退(GC 典型);setDaemon 必须 start 前
- 线程安全三要素:共享资源 + 多线程 + 有修改
- 锁:synchronized 代码块 / 同步方法(this)/ 静态方法(Class)/ Lock 手动档
三、代码模板 4 段(背这几块就够)
① 创建(Runnable + Lambda,标准姿势)
Thread t = new Thread(() -> {
// 工人的活
}, "工人A");
t.start();
② 等结果(join 收账)
t.start();
t.join(); // main 原地等收工,再取结果
③ 定时循环(sleep 节拍器)
while (true) {
System.out.println("滴答 " + new Date());
Thread.sleep(1000); // 抛受检 InterruptedException
}
④ 同步(卖票标准骨架)
synchronized (lock) { // lock = 多线程共用的同一把锁
if (count > 0) {
System.out.println(getName() + " 卖出第 " + count-- + " 张");
}
}
四、名坑回顾(本章五大坑)
- run 直接调——写了一堆多线程代码跑出单线程效果;招工人用
start() - start 调两次——
IllegalThreadStateException;工人不能复活(TERMINATED 终点站) t.sleep()以为睡 t——static 只睡当前线程;写Thread.sleep(...)- 忘 join 就取结果——工人还没算完,拿到的永远是初始值(Demo20 的 0)
- 各锁各的 / Lock 忘 unlock——等于没锁裸奔;手动锁不还,全员永久 BLOCKED
五、收官暗线:收益与代价的交换
整章其实是一场交易:
想要快 → 就得并行 → 并行就得共享资源 → 共享就会被插队 → 就得上锁 → 上锁就牺牲一部分并行度。
从 12.1「多线程让界面不卡」的甜头,到 12.5「锁让并行变排队」的代价,五节走完的是同一条因果链。这也是为什么后面会学线程池(把锁的代价摊薄)、wait/notify(排队时别干烧 CPU)、并发工具类(别人替你把锁用对)——这门课后面所有「高级」的东西,都是在给这条交易链找更聪明的解法。
六、跨章连线
- 第四章 面向对象(下)/4.7 函数式编程:Runnable 是 Lambda 的头号主场
- 第五章 异常/5.2 运行时异常与编译时异常:InterruptedException / IllegalThreadStateException 一受检一运行时,判定法双案例
- 第五章 异常/5.3 异常处理及语法:Lock 的 unlock 放 finally 全靠它
- 第九章 反射机制和注解/9.2 Class类和Class对象:静态同步方法的锁 = 每类一份的 Class 对象
- 第十一章 JDBC/11.4 数据库连接池:「连接不能存成员变量共用」正式结案
- 第十章 I-O/10.2 字节流:try-with-resources 的自动关闭与锁的自动释放异曲同工
七、毕业大题:三窗口卖票(本章集大成)
写一个程序:100 张票、3 个窗口同时卖,要求不重票、不跳号、不卖负数,卖完打印「共耗时」。
自检清单(6 项):
- 票数
count是共享资源 → 必须是静态成员或同一 Runnable 对象的字段 - 用 Runnable + Lambda 创建三个窗口(别占继承名额)
- 卖票的核心代码用
synchronized包住,锁对象三窗口共用同一把 - 判断和扣减必须在同一个同步块里(拆开照样重票)
- count 到 0 时优雅收摊退出循环,别死循环
- 回答:为什么
count--一行也要锁?(答出「读加写三步」才算懂)
✅ 过关标准
三个窗口的票号加起来正好 1~100 各出现一次、无重复无遗漏,跑十次次次如此——你就真正毕业了。
下一步学什么
- ☑ 第十三章 网络编程 · 13.1 网络基础 ✅ —— 点此复习:寄快递三要素(IP/端口/协议),Java 从单机走向互联网