Java 中 wait() 和 sleep() 的区别?
Java中wait()和sleep()的关键区别:wait()是Object类方法,必须在同步代码块中调用,会释放对象锁,需通过notify()唤醒;sleep()是Thread类静态方法,可在任意位置调用,不释放锁,超时自动恢复。主要差异体现在锁释放机制、使用环境和唤醒方式上。wait()用于线程间通信,sleep()仅用于线程暂停。
·
Java 中 wait() 和 sleep() 的主要区别如下:
1. 基本定义
wait() 方法
wait()是Object类的方法- 用于线程间通信,让线程释放对象锁并等待
- 必须在同步代码块或同步方法中调用
- 需要配合
synchronized关键字使用
sleep() 方法
sleep()是Thread类的静态方法- 用于让线程暂停执行指定时间
- 可以在任何地方调用
- 不需要同步环境
2. 核心区别对比表
| 特性 | wait() | sleep() |
|---|---|---|
| 所属类 | Object 类 | Thread 类 |
| 调用方式 | 实例方法 | 静态方法 |
| 锁释放 | ✅ 释放对象锁 | ❌ 不释放锁 |
| 使用环境 | 必须在 synchronized 中 | 任何地方 |
| 唤醒方式 | notify()/notifyAll() 或超时 | 超时自动唤醒 |
| 异常处理 | 检查异常 InterruptedException | 检查异常 InterruptedException |
| 线程状态 | WAITING/TIMED_WAITING | TIMED_WAITING |
| 用途 | 线程间通信 | 线程休眠 |
3. 详细对比
3.1 锁的释放
public class LockReleaseDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
// wait() 释放锁
new Thread(() -> {
synchronized (lock) {
System.out.println("Thread1: 获取锁");
try {
System.out.println("Thread1: 调用 wait(),释放锁");
lock.wait(); // 释放锁,进入等待状态
System.out.println("Thread1: 被唤醒,重新获取锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// sleep() 不释放锁
new Thread(() -> {
synchronized (lock) {
System.out.println("Thread2: 获取锁");
try {
System.out.println("Thread2: 调用 sleep(),不释放锁");
Thread.sleep(2000); // 不释放锁
System.out.println("Thread2: sleep 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
3.2 使用环境
public class UsageEnvironmentDemo {
private static final Object lock = new Object();
// wait() 必须在 synchronized 中使用
public void waitExample() {
synchronized (lock) {
try {
lock.wait(); // ✅ 正确
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// wait() 不在 synchronized 中会抛出异常
public void waitErrorExample() {
try {
lock.wait(); // ❌ 运行时抛出 IllegalMonitorStateException
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// sleep() 可以在任何地方使用
public void sleepExample() {
try {
Thread.sleep(1000); // ✅ 正确
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// sleep() 也可以在 synchronized 中使用
public void sleepInSyncExample() {
synchronized (lock) {
try {
Thread.sleep(1000); // ✅ 正确,但不释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.3 唤醒方式
public class WakeupDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
// 等待线程
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
System.out.println("等待线程: 开始等待");
try {
lock.wait(); // 等待被唤醒
System.out.println("等待线程: 被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 唤醒线程
Thread notifyingThread = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(1000); // 确保等待线程先执行
System.out.println("唤醒线程: 准备唤醒");
lock.notify(); // 唤醒等待线程
System.out.println("唤醒线程: 已发送唤醒信号");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
waitingThread.start();
notifyingThread.start();
}
}
3.4 超时等待
public class TimeoutDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
// wait() 带超时
new Thread(() -> {
synchronized (lock) {
System.out.println("Thread1: 开始等待,最多3秒");
try {
lock.wait(3000); // 等待3秒后自动唤醒
System.out.println("Thread1: 等待结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// sleep() 带超时
new Thread(() -> {
System.out.println("Thread2: 开始休眠3秒");
try {
Thread.sleep(3000); // 休眠3秒后自动唤醒
System.out.println("Thread2: 休眠结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
4. 实际应用场景
4.1 wait() 的典型应用 - 生产者消费者模式
public class ProducerConsumerDemo {
private static final int MAX_SIZE = 5;
private static final List<Integer> buffer = new ArrayList<>();
private static final Object lock = new Object();
// 生产者
static class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
while (buffer.size() >= MAX_SIZE) {
try {
System.out.println("缓冲区满,生产者等待");
lock.wait(); // 缓冲区满时等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
buffer.add(i);
System.out.println("生产: " + i + ",当前大小: " + buffer.size());
lock.notifyAll(); // 通知消费者
}
try {
Thread.sleep(100); // 模拟生产耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 消费者
static class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
while (buffer.isEmpty()) {
try {
System.out.println("缓冲区空,消费者等待");
lock.wait(); // 缓冲区空时等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int value = buffer.remove(0);
System.out.println("消费: " + value + ",当前大小: " + buffer.size());
lock.notifyAll(); // 通知生产者
}
try {
Thread.sleep(200); // 模拟消费耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
}
4.2 sleep() 的典型应用 - 模拟耗时操作
public class SleepDemo {
// 模拟网络请求
public static String fetchDataFromServer() {
try {
System.out.println("开始请求数据...");
Thread.sleep(2000); // 模拟网络延迟
System.out.println("数据请求完成");
return "模拟数据";
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
return null;
}
}
// 定时任务
public static void scheduledTask() {
while (true) {
System.out.println("执行定时任务: " + new Date());
try {
Thread.sleep(5000); // 每5秒执行一次
} catch (InterruptedException e) {
System.out.println("定时任务被中断");
break;
}
}
}
// 限流控制
public static void rateLimitedRequest(int requestCount) {
for (int i = 0; i < requestCount; i++) {
System.out.println("发送请求 " + (i + 1));
try {
Thread.sleep(1000); // 每秒最多1个请求
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
public static void main(String[] args) {
// 示例1:模拟网络请求
String data = fetchDataFromServer();
System.out.println("获取到的数据: " + data);
// 示例2:限流控制
rateLimitedRequest(3);
}
}
5. 线程状态变化
5.1 wait() 的状态变化
RUNNING → synchronized块 → 调用wait() → WAITING状态
↓
notify()/notifyAll()
↓
获取锁 → RUNNABLE → RUNNING
5.2 sleep() 的状态变化
RUNNING → 调用sleep() → TIMED_WAITING状态
↓
超时时间到
↓
RUNNABLE → RUNNING
6. 最佳实践
6.1 wait() 的最佳实践
public class WaitBestPractices {
private final Object lock = new Object();
private boolean condition = false;
// ✅ 正确的 wait() 使用方式
public void correctWaitUsage() {
synchronized (lock) {
// 使用 while 循环检查条件,防止虚假唤醒
while (!condition) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// 处理中断
return;
}
}
// 执行业务逻辑
}
}
// ❌ 错误的 wait() 使用方式
public void incorrectWaitUsage() {
synchronized (lock) {
if (!condition) { // 应该使用 while
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 正确的 notify() 使用方式
public void correctNotifyUsage() {
synchronized (lock) {
condition = true;
lock.notifyAll(); // 优先使用 notifyAll()
}
}
}
6.2 sleep() 的最佳实践
public class SleepBestPractices {
// ✅ 正确的 sleep() 使用方式
public void correctSleepUsage() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
// 处理中断逻辑
System.out.println("线程被中断");
}
}
// ❌ 错误的 sleep() 使用方式
public void incorrectSleepUsage() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(); // 只是打印异常,不处理中断
}
}
// 使用 TimeUnit 提高可读性
public void readableSleep() {
try {
TimeUnit.SECONDS.sleep(1); // 比 Thread.sleep(1000) 更清晰
TimeUnit.MILLISECONDS.sleep(500);
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
7. 常见面试题
Q1: 为什么 wait() 要在 synchronized 中调用?
A:
wait()需要释放对象锁,只有持有锁的线程才能释放锁- 如果不在 synchronized 中调用,会抛出
IllegalMonitorStateException - 这是为了确保线程安全,防止竞争条件
Q2: wait() 和 sleep() 都会抛出 InterruptedException,如何处理?
A:
- 捕获异常后应该恢复线程的中断状态:
Thread.currentThread().interrupt() - 根据业务需求决定是继续执行还是退出
- 不要简单地忽略中断异常
Q3: 为什么推荐使用 while 循环检查 wait() 的条件?
A:
- 防止虚假唤醒(spurious wakeup)
- 确保条件真正满足后才继续执行
- 避免因意外唤醒导致的逻辑错误
Q4: notify() 和 notifyAll() 有什么区别?
A:
notify(): 随机唤醒一个等待线程notifyAll(): 唤醒所有等待线程- 推荐使用
notifyAll(),避免线程饥饿问题
8. 总结
| 方面 | wait() | sleep() |
|---|---|---|
| 核心用途 | 线程间通信 | 线程休眠 |
| 锁处理 | 释放锁 | 不释放锁 |
| 使用场景 | 需要协调的并发场景 | 简单的延时控制 |
| 复杂度 | 较复杂,需要配合同步 | 简单,直接调用 |
| 推荐使用 | 生产者消费者、条件等待 | 定时任务、限流控制 |
选择原则:
- 需要线程间协调通信 → 使用
wait() - 只需要简单的延时 → 使用
sleep() - 在同步环境中需要等待条件 → 使用
wait() - 在任何地方需要暂停 → 使用
sleep()
理解两者的区别对于编写正确的并发程序至关重要,合理使用可以有效解决线程同步和协调问题。
更多推荐



所有评论(0)