一、车间里的工人:线程的日常工作
在 Java 的 "工厂车间" 里,每个线程都是一个忙碌的工人。他们需要协作完成任务,而wait、notify、sleep、interrupt等方法就是他们之间的 "工作指令"。让我们通过车间场景理解这些指令的作用。
1. sleep:工人的强制休息
sleep就像工人累了,需要强制休息一段时间,休息时不释放手中的工具(锁):
java
// 工人A:每工作2秒休息1秒
class WorkerA implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("WorkerA: 正在加工零件 " + i);
try {
Thread.sleep(1000); // 休息1秒,不释放锁
} catch (InterruptedException e) {
System.out.println("WorkerA: 被打断休息");
e.printStackTrace();
}
}
}
}
// 启动工人A
new Thread(new WorkerA()).start();
特点:
- 属于
Thread类的静态方法 - 休息时不释放工具(锁)
- 到时间自动醒来,或被
interrupt打断
2. wait:等待材料的工人
wait就像工人在等待原材料,需要放下工具(释放锁),直到材料到达被通知:
java
// 原材料仓库
class MaterialWarehouse {
private boolean hasMaterial = false;
private final Object lock = new Object(); // 仓库门锁
// 工人等待材料
public void waitForMaterial() {
synchronized (lock) { // 获得仓库门锁
System.out.println("工人: 等待原材料...");
try {
lock.wait(); // 放下工具,等待通知
System.out.println("工人: 收到原材料通知,继续工作");
} catch (InterruptedException e) {
System.out.println("工人: 等待被中断");
e.printStackTrace();
}
}
}
// 通知工人材料已到
public void notifyMaterialArrived() {
synchronized (lock) {
hasMaterial = true;
lock.notify(); // 通知一个等待的工人
// lock.notifyAll(); // 通知所有等待的工人
System.out.println("仓库: 原材料已送达,通知工人");
}
}
}
// 使用示例
MaterialWarehouse warehouse = new MaterialWarehouse();
new Thread(() -> warehouse.waitForMaterial()).start();
// 5秒后通知材料到达
Thread.sleep(5000);
warehouse.notifyMaterialArrived();
特点:
- 属于
Object类的实例方法 - 必须在
synchronized块中使用 - 等待时释放工具(锁)
- 可被
notify/notifyAll唤醒,或超时醒来,或被interrupt打断
二、车间调度指令:线程控制方法
1. interrupt:车间里的紧急呼叫
interrupt就像车间管理员大喊一声,通知工人中断当前等待或休息:
java
class InterruptibleWorker implements Runnable {
private Thread thread;
@Override
public void run() {
thread = Thread.currentThread();
for (int i = 0; i < 10; i++) {
System.out.println("Worker: 工作中 " + i);
try {
// 模拟等待或休息
if (i == 5) {
System.out.println("Worker: 开始等待材料");
synchronized (this) {
this.wait(); // 等待时可被interrupt打断
}
} else {
Thread.sleep(1000); // 休息时可被interrupt打断
}
} catch (InterruptedException e) {
System.out.println("Worker: 收到中断通知,结束工作");
thread.interrupt(); // 重新设置中断标志
return;
}
}
}
}
// 启动并中断工人
InterruptibleWorker worker = new InterruptibleWorker();
Thread workerThread = new Thread(worker);
workerThread.start();
Thread.sleep(3000);
workerThread.interrupt(); // 3秒后中断工人
关键原理:
- 不直接终止线程,而是设置中断标志
- 仅当线程在
wait/sleep/join时才会抛出异常 - 非阻塞状态下调用
interrupt仅设置标志,需手动检查
2. yield:工人的短暂礼让
yield就像工人暂时停下,让其他工人先干,但可能很快又继续:
java
class YieldingWorker implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Worker: 工作进度 " + i);
if (i % 2 == 0) {
System.out.println("Worker: 礼让其他工人");
Thread.yield(); // 礼让CPU
}
}
}
}
// 启动两个工人,观察礼让效果
new Thread(new YieldingWorker()).start();
new Thread(new YieldingWorker()).start();
特点:
- 从
Running状态转为Runnable,可能很快再次运行 - 不释放锁,仅出让 CPU 时间
- 无法保证礼让成功,取决于调度器
三、车间协作规则:关键区别对比
| 指令 | 角色 | 工具处理 | 唤醒方式 | 适用场景 |
|---|---|---|---|---|
sleep | 休息的工人 | 不放下工具 | 时间到 / 被呼叫 (interrupt) | 线程内定时休息 |
wait | 等材料的工人 | 放下工具 | 材料到 (notify)/ 全部到 (notifyAll)/ 时间到 / 被呼叫 | 线程间协作等待 |
interrupt | 管理员呼叫 | - | 通知工人中断等待 | 紧急情况中断线程 |
yield | 礼让的工人 | 不放下工具 | 自动重新获取 CPU | 平衡线程调度 |
核心代码对比:wait 与 sleep
java
// wait使用示例(需在synchronized块中)
synchronized (lock) {
lock.wait(); // 放下工具,等待通知
}
// sleep使用示例
Thread.sleep(1000); // 休息1秒,不释放锁
四、车间管理最佳实践
-
wait/notify 协作模式:
java
// 标准等待-通知模式 synchronized (monitor) { while (条件不满足) { monitor.wait(); // 等待条件满足 } // 处理任务 } // 通知方 synchronized (monitor) { 改变条件; monitor.notify(); // 通知等待者 } -
中断处理最佳实践:
java
public void run() { while (!Thread.currentThread().isInterrupted()) { try { doWork(); Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重新设置中断标志 break; } } } -
避免死锁提示:
-
等待时使用
wait(timeout)设置超时 -
通知时优先使用
notifyAll避免个别线程饥饿 -
中断处理时及时释放资源
-
通过这个车间故事,我们理解了 Java 线程协作的核心机制。在实际开发中,合理使用这些 "调度指令" 可以让多线程协作更加高效,避免 "工人拥堵" 和 "资源浪费",打造流畅的多线程应用。