我们知道,在 Java 中,通过 Thread.sleep() 和 Object 的 wait 方法都可以阻塞当前线程的执行。但由于sleep() 不会释放锁对象,而 wait()会释放锁对象,因此在多线程并发的环境中,它们的效果,是截然不同的。并且,用法上也有一定区别:sleep 阻塞时间到了以后,会自动继续往下执行;但使用 wait 时,则必须使用 notify 或 notifyAll 来唤醒。下面通过一段实验代码来验证记录:
public class SleepAndWait {
public static void main(String[] args) {
SleepAndWait sleepAndWait = new SleepAndWait();
new Thread(() -> {
sleepAndWait.sleepOrWait();
}).start();
new Thread(() -> {
sleepAndWait.func();
}).start();
}
private void sleepOrWait() {
synchronized (this) {
System.out.println("1. before block");
try {
// 1. 验证 sleep 不会释放锁
Thread.sleep(500);
// 2. 验证 wait 会释放锁
// 因为 wait 释放了锁,所以并发时 func 会立即得到执行
// this.wait();
System.out.println("3. after block");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void func() {
synchronized (this) {
System.out.println("2. call func");
this.notify();
}
}
}
首先我们使用 sleep() 进行阻塞,结果输出:
1. before block
3. after block
2. call func
说明:因为 sleepOrWait
和 func
分别由不同的线程执行,但是他们的 synchronized 使用了同一个锁对象,而且因为 sleep()
方法不会释放对象锁,所以能保证并发时是按顺序先执行完了 sleepOrWait
同步块内的所有代码,然后 func
中的同步块才会被执行,所以顺序是 1 -> 3 ->2。
然后我们修改由 wait() 进行阻塞,结果将输出:
1. before block
2. call func
3. after block
说明:因为 wait()
方法会释放当前的锁对象,所以虽然 sleepOrWait
同步块内的 wait() 方法后还有语句未执行,但是由于 wait() 的阻塞作用,并且已经释放了锁对象,所以 func 中被阻塞的同步块获得锁对象开始执行,并且通过调用同一个对象的 notify 方法,唤醒执行 sleepOrWait 的线程继续执行,故输出顺序是1 -> 2 -> 3。
如果 wait 没有释放锁对象这一特性,那么就只会输出1了,2和3将永远得不到执行。