在 Java 中,wait
和 sleep
都用于让线程进入等待状态,但它们在同步操作和线程管理方面有显著区别。以下是它们的主要区别:
1. 所属的类
wait
是Object
类的方法。sleep
是Thread
类的静态方法。
2. 使用场景
wait
用于线程间通信,它通常在同步代码块或同步方法中使用,以便让当前线程等待,直到另外一个线程调用同一对象的notify
或notifyAll
方法。sleep
用于让当前线程暂停执行一段时间,通常用于控制线程的执行节奏或模拟延迟。
3. 锁的释放
wait
会释放当前线程持有的对象锁,这样其他线程可以获得这个锁并继续执行。sleep
不会释放当前线程持有的任何锁。线程在休眠期间依然持有锁,其他线程无法获得该锁。
4. 重新唤醒
wait
需要其他线程调用同一对象的notify
或notifyAll
方法来唤醒。sleep
在指定的时间段结束后自动唤醒。
5. 使用位置
wait
必须在同步代码块或同步方法中使用,否则会抛出IllegalMonitorStateException
。sleep
可以在任何地方使用,不需要同步块或同步方法。
关于锁的释放,举个下面的代码例子:
package chapter01;class Test {public static void main(String[] args) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}class WaitNotifyExample {private final Object lock = new Object();public void waitingMethod() {synchronized (lock) {try {System.out.println(Thread.currentThread().getName() + " is waiting...");lock.wait();System.out.println(Thread.currentThread().getName() + " is resumed.");} catch (InterruptedException e) {e.printStackTrace();}}}public void notifyingMethod() {synchronized (lock) {System.out.println(Thread.currentThread().getName() + " is notifying...");lock.notify();}}public static void main(String[] args) throws InterruptedException {WaitNotifyExample example = new WaitNotifyExample();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {example.waitingMethod();}}, "Thread-1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {example.notifyingMethod();}}, "Thread-2");t1.start();Thread.sleep(1000); // Ensure t1 starts and calls waitt2.start();}
}
wait 和 notify 示例
- WaitNotifyExample 类包含两个方法:waitingMethod 和 notifyingMethod。
- waitingMethod 在同步代码块中调用 wait 方法,使当前的线程1等待并释放锁lock。
- 因为释放了锁lock,所以线程2能够在 notifyingMethod 方法中进入同步代码块中调用 notify 方法,唤醒等待的线程。
- 在 main 方法中,两个线程 t1 和 t2 分别调用 waitingMethod 和 notifyingMethod,演示线程间的通信。
与之对比,sleep方法则不会让线程释放掉当前持有的锁
class SleepExample {private final Object lock = new Object();public void sleepMethod() {synchronized (lock) {try {System.out.println(Thread.currentThread().getName() + " got the lock and is sleeping...");Thread.sleep(2000); // Sleep for 2 secondsSystem.out.println(Thread.currentThread().getName() + " woke up and is releasing the lock.");} catch (InterruptedException e) {e.printStackTrace();}}}public void anotherMethod() {synchronized (lock) {System.out.println(Thread.currentThread().getName() + " got the lock and is doing work.");}}public static void main(String[] args) {SleepExample example = new SleepExample();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {example.sleepMethod();}}, "Thread-1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {example.anotherMethod();}}, "Thread-2");t1.start();try {Thread.sleep(500); // Ensure t1 starts and gets the lock} catch (InterruptedException e) {e.printStackTrace();}t2.start();}
}
可以发现线程1得到了锁,然后调用了sleep,但是这时候并没有释放掉锁lock,所以线程2必须等到线程1睡醒了执行完代码然后释放锁lock才能进入 anotherMethod 的 synchronized 的同步代码块。