在 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() {
@Override
public void run() {
example.waitingMethod();
}
}, "Thread-1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
example.notifyingMethod();
}
}, "Thread-2");
t1.start();
Thread.sleep(1000); // Ensure t1 starts and calls wait
t2.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 seconds
System.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() {
@Override
public void run() {
example.sleepMethod();
}
}, "Thread-1");
Thread t2 = new Thread(new Runnable() {
@Override
public 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 的同步代码块。