Thread.sleep()、Object.wait()、Thread.yield() 和 Thread.join() 的区别是什么?
这四个方法都是 Java 中用于控制线程状态和调度的核心方法,但它们在所属类、是否释放锁、使用场景和底层机制上有很大的区别。
以下是它们的详细对比和解析:
1. Thread.sleep(long millis)
- 所属类:
java.lang.Thread(静态方法) - 作用:让当前正在执行的线程暂停执行指定的时间,进入阻塞状态(
TIMED_WAITING)。时间到了之后,线程会自动苏醒并进入就绪状态(RUNNABLE),等待 CPU 再次调度。 - 关于锁:绝对不会释放任何锁。如果线程在同步块(
synchronized)中调用了sleep(),它会“抱着锁睡觉”,其他等待该锁的线程依然无法进入同步块。 - 使用场景:通常用于模拟耗时操作、定时任务、或者让出 CPU 给其他线程执行(但保留锁的使用权)。
2. Object.wait()
- 所属类:
java.lang.Object(实例方法) - 作用:让当前线程进入等待状态(
WAITING或TIMED_WAITING),直到其他线程调用同一个对象上的notify()或notifyAll()方法将其唤醒。 - 关于锁:会释放当前对象的监视器锁(Monitor Lock)。正因为要释放锁,所以
wait()必须且只能在synchronized同步代码块或同步方法中使用,否则会抛出IllegalMonitorStateException异常。 - 使用场景:主要用于线程间通信(如经典的“生产者-消费者”模型)。
3. Thread.yield()
- 所属类:
java.lang.Thread(静态方法) - 作用:向操作系统的线程调度器发出一个“让步”暗示:当前线程愿意放弃当前分配到的 CPU 时间片。
- 关于锁:不会释放任何锁。
- 状态变化:调用
yield()后,线程从“运行中(Running)”退回到“就绪状态(Ready)”(在 Java 线程状态中统称为RUNNABLE)。 - 注意点:这只是一个暗示(Hint),调度器完全可以忽略这个请求。即使让出了 CPU,该线程也可能立刻再次被操作系统选中执行。通常只让步于相同或更高优先级的线程。
- 使用场景:极少在业务代码中使用,偶尔用于并发测试、调试,或者在自旋锁(Spinlock)中减少 CPU 消耗,防止一个线程长时间霸占 CPU。
4. Thread.join()
- 所属类:
java.lang.Thread(实例方法) - 作用:让当前线程(调用
join()所在的代码块的线程)陷入等待,直到目标线程(调用join()方法的那个线程对象)执行完毕。 - 关于锁:
join()底层其实是调用了Object.wait()方法来实现的。它会释放当前 Thread 对象的锁,但不会释放当前线程所持有的其他业务对象的锁。 - 使用场景:用于处理线程间的依赖关系。例如,主线程需要等待子线程计算出结果后,才能继续往下执行汇总操作。
核心区别总结表
| 特性 | Thread.sleep() |
Object.wait() |
Thread.yield() |
Thread.join() |
|---|---|---|---|---|
| 所属类 | Thread (静态方法) |
Object (实例方法) |
Thread (静态方法) |
Thread (实例方法) |
| 是否释放锁 | 不释放 ("抱着锁睡觉") | 释放锁 (必须在同步块中) | 不释放 | 不释放外部锁 (底层调用wait,释放线程对象本身的锁) |
| 必须在 synchronized 中使用? | 否 | 是 | 否 | 否 |
| 唤醒条件 | 时间到 / 被中断 | notify() / notifyAll() / 时间到 |
调度器重新分配时间片 | 目标线程执行结束 |
| 当前线程状态转变 | RUNNABLE -> TIMED_WAITING |
RUNNABLE -> WAITING/TIMED_WAITING |
RUNNABLE -> RUNNABLE (Running -> Ready) |
RUNNABLE -> WAITING/TIMED_WAITING |
| 主要设计目的 | 暂停当前线程执行 | 多线程间通信/协作 | 让出CPU时间片,避免过度占用 | 等待其他线程执行完毕 |
经典面试题:sleep() 和 wait() 的区别?
这是最常被问到的对比,可以简单归纳为“三个不同”:
- 出处不同:
sleep()是Thread类的静态方法;wait()是Object类的实例方法。 - 对锁的处理不同(最核心):
sleep()不释放锁;wait()释放锁。 - 使用位置和唤醒方式不同:
sleep()可以在任何地方使用,靠时间自动唤醒;wait()必须在synchronized代码块中使用,通常靠别人(notify)唤醒。
右滑查看面试常问