Java初级并发知识【3】,看看你还记得多少?
你好,我是一航同学,AI发展很快,快到我们已经忽略了很多基础知识的构建。俗话说,基础不牢地动山摇。在快速迭代的AI时代,请沉下心,耐心看完,查缺补漏。
Java初级并发知识【3】
线程生命周期
核心概念
| 概念 | 核心要点 | 记忆口诀 |
|---|---|---|
| 线程状态 | 6 种状态:NEW / RUNNABLE / BLOCKED / WAITING / TIMED_WAITING / TERMINATED | “新建→就绪→运行→等待→阻塞→死亡” |
| sleep vs wait | sleep 不释放锁 + 时间到自动醒;wait 释放锁 + 需被唤醒 | “sleep 是’暂停’,wait 是’让位’” |
| wait/notify | 必须在同步块中使用,配合 while 循环防虚假唤醒 | “等条件→wait();改条件→notify()” |
线程状态是"结果",不是"原因"。 我们通过调用方法(如 wait()、sleep())让线程进入某状态,而不是直接"设置状态"。
生活类比:地铁安检 + 候车室
1. 6 种状态类比
| 状态 | 类比 | 说明 |
|---|---|---|
| NEW(新建) | 乘客买了票,还没进地铁站 | new Thread() 后未 start() |
| RUNNABLE(可运行) | 乘客在安检口排队(就绪)或正在过安检(执行) | 包含"就绪"和"运行"两种子状态,由 OS 调度决定 |
| BLOCKED(阻塞) | 安检口被占用,乘客等待 | 专指等待 synchronized 锁,不是所有等待都叫 BLOCKED |
| WAITING(等待) | 乘客在候车室等广播通知 | 无超时等待:wait() / join() |
| TIMED_WAITING | 乘客设了 10 分钟闹钟小睡 | 有超时等待:sleep(ms) / wait(ms) |
| TERMINATED(终止) | 乘客出站,旅程结束 | run() 执行完或异常退出 |
WAITING vs TIMED_WAITING: 区别仅在于有没有设置超时时间。
2. wait/notify 角色类比
| 角色 | 类比 | 对应概念 |
|---|---|---|
| 乘客 A | 线程 A | 执行 wait() 进入候车室(释放锁 + 等待) |
| 广播员 | 线程 B | 执行 notify() 唤醒候车室乘客(但不释放当前锁) |
| 候车条件 | while(!条件) |
防止"虚假唤醒":醒了但条件仍不满足,继续等 |
wait/notify = 候车室广播系统:
wait()必须配合 while 循环,不能用 if(防止虚假唤醒)notify()随机唤醒一个等待线程,notifyAll()唤醒所有(更安全)- 唤醒后线程要重新竞争锁,不是立即执行
Java 代码视角
1. 线程 6 大状态 + 触发方法总览
public enum State {
NEW, // new Thread() 后,未 start()
RUNNABLE, // start() 后,或运行中/就绪中
BLOCKED, // 等待 synchronized 锁(进入同步块时锁被占用)
WAITING, // 无超时等待:Object.wait() / Thread.join() / LockSupport.park()
TIMED_WAITING, // 有超时等待:Thread.sleep(ms) / Object.wait(ms) / join(ms)
TERMINATED // run() 方法执行完,或异常退出
}
public class StateDemo {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
// ① RUNNABLE:正在执行
System.out.println("当前状态: " + Thread.currentThread().getState());
// ② TIMED_WAITING:sleep
try { Thread.sleep(1000); } catch (InterruptedException e) {}
// ③ BLOCKED:等待 synchronized 锁
synchronized (StateDemo.class) {
// 临界区代码
}
// ④ WAITING:wait()
synchronized (StateDemo.class) {
try { StateDemo.class.wait(); } catch (InterruptedException e) {}
}
});
// ⑤ NEW:刚创建
System.out.println("NEW: " + thread.getState());
thread.start();
// ⑥ RUNNABLE:start() 后
System.out.println("RUNNABLE: " + thread.getState());
thread.join();
// ⑦ TERMINATED:执行完
System.out.println("TERMINATED: " + thread.getState());
}
}
实际开发中很少直接判断线程状态,但需要能看懂 jstack 日志中的状态,用于排查死锁/卡顿。
2. sleep() vs wait()
| 对比项 | Thread.sleep(ms) | Object.wait(ms) |
|---|---|---|
| 所属类 | Thread 静态方法 | Object 实例方法 |
| 锁的处理 | 不释放锁 | 释放当前持有的锁 |
| 唤醒方式 | 时间到自动唤醒 | 需 notify()/notifyAll() 或超时 |
| 使用范围 | 任意位置 | 必须在 synchronized 块/方法中 |
| 异常处理 | 需捕获 InterruptedException | 需捕获 InterruptedException |
| 典型场景 | 模拟延迟、轮询间隔 | 线程协作、生产者消费者 |
// sleep:不释放锁,其他线程进不来
synchronized (lock) {
System.out.println("A: 持有锁,开始sleep");
Thread.sleep(2000); // 锁仍被 A 持有
System.out.println("A: sleep结束");
}
// wait:释放锁,其他线程可以进入
synchronized (lock) {
System.out.println("B: 持有锁,开始wait");
lock.wait(2000); // 释放锁,进入等待
System.out.println("B: 被唤醒,重新竞争锁");
}
不要在循环外调用 wait()! 防止"虚假唤醒"(线程被唤醒但条件仍不满足)。
3. wait/notify 正确用法:生产者-消费者简化版
public class ProducerConsumer {
private final Queue<Integer> queue = new LinkedList<>();
private final int MAX_SIZE = 5;
// 生产者
public void produce(int value) throws InterruptedException {
synchronized (this) {
while (queue.size() >= MAX_SIZE) { // 关键:用 while 判断条件,不是 if!
System.out.println("队列满,生产者等待...");
this.wait(); // 释放锁,等待消费者通知
}
queue.offer(value);
System.out.println("生产: " + value);
this.notifyAll(); // 通知所有等待的线程(消费者)
}
}
// 消费者
public int consume() throws InterruptedException {
synchronized (this) {
while (queue.isEmpty()) {
System.out.println("队列空,消费者等待...");
this.wait(); // 释放锁,等待生产者通知
}
int value = queue.poll();
System.out.println("消费: " + value);
this.notifyAll(); // 通知所有等待的线程(生产者)
return value;
}
}
}
为什么用 while 而不是 if?
// 错误:用 if 可能"虚假唤醒"
if (queue.isEmpty()) { wait(); }
// 场景:线程A被 notify() 唤醒,但线程B抢先消费了数据,队列仍空 → A继续执行会报错!
// 正确:用 while 循环检查
while (queue.isEmpty()) { wait(); }
// 即使被唤醒,也会重新检查条件,不满足继续等
实际项目中优先用 BlockingQueue 替代手写 wait/notify,更安全简洁!
4. 状态转换图
start()
┌─────────────┐
▼ │
NEW ──► RUNNABLE ◄──┐
│ │
获得synchronized锁 │
▼ │
BLOCKED ────┘
│
调用wait()/join() │
▼
WAITING ──► notify()/notifyAll()
│ │
sleep(ms)/wait(ms) │
▼ │
TIMED_WAITING ──────┘
│
run()结束/异常
▼
TERMINATED
对比表:常见方法触发状态速查
| 方法 | 触发状态 | 是否释放锁 | 初级使用建议 |
|---|---|---|---|
| Thread.sleep(ms) | RUNNABLE → TIMED_WAITING | 不释放 | 模拟延迟、限流 |
| Object.wait() | RUNNABLE → WAITING | 释放 | 线程协作(配合 notify) |
| Object.wait(ms) | RUNNABLE → TIMED_WAITING | 释放 | 带超时的等待 |
| Thread.join() | RUNNABLE → WAITING | 不释放(等待其他线程结束) | 主线程等待子任务完成 |
| synchronized 竞争失败 | RUNNABLE → BLOCKED | - | 自动触发,无需手动调用 |
| LockSupport.park() | RUNNABLE → WAITING | 不释放 | 高级并发工具底层使用 |
高频面试题
Q1:sleep() 和 wait() 有什么区别?(必考!)
- 所属类不同: sleep 是 Thread 的静态方法;wait 是 Object 的实例方法
- 锁的处理不同(核心区别): sleep 不释放锁;wait 释放锁,允许其他线程竞争
- 使用范围不同: sleep 可在任何地方调用;wait 必须在 synchronized 块/方法中调用
- 唤醒方式不同: sleep 时间到自动唤醒;wait 需 notify/notifyAll 或超时
- 异常处理: 两者都需捕获 InterruptedException
Q2:为什么 wait() 要放在 while 循环里,而不是 if?
因为存在虚假唤醒(Spurious Wakeup):线程可能在没有被 notify() 的情况下被唤醒(JVM/OS 底层机制)。
// 错误:if 写法,可能被虚假唤醒,条件仍不满足就继续执行
if (queue.isEmpty()) { wait(); }
// 唤醒后直接执行 poll(),但队列可能还是空 → 报错!
// 正确:while 写法,唤醒后重新检查条件
while (queue.isEmpty()) { wait(); }
// 即使被唤醒,条件不满足会继续等待,保证安全
Q3:线程被 notify() 唤醒后,能立即执行吗?
不能!notify() 只是把线程从等待池移到锁池,线程还需要重新竞争 synchronized 锁,竞争成功才能继续执行。
类比:候车室广播叫号(notify)→ 乘客起身(被唤醒)→ 还要排队过安检(竞争锁)→ 才能继续旅程(执行代码)。
Q4:Thread.yield() 是干嘛的?
- 作用: 提示调度器"我愿意让出 CPU",但调度器不一定采纳(依赖操作系统实现)
- 状态变化: RUNNABLE → RUNNABLE(仍在就绪队列,可能立即又被调度)
- 建议: 知道有这个概念即可,实际开发极少用,线程调度交给 JVM/OS 更可靠
自测练习
- 【判断】 线程调用
sleep()时会释放锁吗?wait()呢? - 【选择】 以下哪种情况线程会进入 BLOCKED 状态?A. 调用
Thread.sleep(1000)B. 竞争 synchronized 锁失败 C. 调用Object.wait() - 【简答】 为什么
notifyAll()比notify()更安全?什么场景必须用notifyAll()? - 【代码】 修复下面代码的潜在问题:
synchronized (lock) {
if (!condition) {
lock.wait();
}
// 执行后续逻辑
}
答案
sleep()不释放锁;wait()释放锁。 这是两者最核心区别!- B。 BLOCKED 专指等待 synchronized 锁;A 是 TIMED_WAITING,C 是 WAITING。
notifyAll()唤醒所有等待线程,避免"漏唤醒"。 场景:多个条件变量共用一个锁时(如生产者-消费者),必须用notifyAll(),否则可能唤醒错误条件的线程导致死锁。- 修复:if 改为 while,防止虚假唤醒:
synchronized (lock) {
while (!condition) { // 关键!
lock.wait();
}
// 执行后续逻辑
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)