Sleep 和 Wait 的区别、比较与简单使用

HYF Lv3

sleep()wait() 都是与线程(或进程)等待(休眠)一段时间相关的操作,且 sleep() 和 wait() 方法都可以响应 interrupt 中断,也就是线程在休眠的过程中,如果收到中断信号,都可以进行响应,并抛出 InterruptedException 异常。让我们来看看它们的不同之处。

区别一:语法使用不同

Thead.Sleep() 代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
private static void sleep() {
try {
System.out.println("Sleep开始");
stopWatch.start();
Thread.sleep(2000);
stopWatch.stop();
System.out.println("Sleep结束");
System.out.println(stopWatch.getTotalTimeSeconds() + "秒");
} catch (Exception e) {
e.printStackTrace();
}
}

Object.Wait() 方法必须配合 synchronized 一起使用,不然在运行时就会抛出 IllegalMonitorStateException 的异常,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private static void Wait() {
System.out.println("Wait开始");
stopWatch.start();
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
try {
lock.wait(2000);
stopWatch.stop();
System.out.println("Wait结束");
System.out.println(stopWatch.getTotalTimeSeconds() + "秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();

// 在另一个线程中,当某些条件满足时,唤醒等待的线程
new Thread(() -> {
synchronized (lock) {
try {
lock.wait(1000);
// 或 lock.notifyAll(),唤醒所有等待线程
lock.notify();
System.out.println("线程被唤醒");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}

这段代码展示了一个简单的Java程序,其中包含了使用wait()和notify()的线程等待和唤醒的示例。让我逐步解释这段代码的流程:

1.Wait方法开始执行,Wait开始输出。
2.第一个线程进入synchronized (lock)块,调用lock.wait(2000)并等待2秒。
3.第二个线程进入synchronized (lock)块,调用lock.wait(1000)并等待1秒。
4.第二个线程调用lock.notify()来唤醒等待的线程。
5.第一个线程被唤醒,计算等待时间,输出”Wait结束”和等待时间。
6.第二个线程输出”线程被唤醒”。

需要注意的是,wait() 和 notify() 方法必须在 synchronized 块中调用,并且需要使用相同的锁(示例里使用的是 lock 对象)。
执行结果:

1
2
3
4
5
6
Wait开始
线程被唤醒
Wait结束
1.0444223秒

Process finished with exit code 0

区别二:所属类不同

Sleep() 属于 Thread 类的方法,而 Wait() 属于 Object 类的方法。

区别三:唤醒方式不同

sleep() 方法需要传递一个休眠时间的参数,用于让当前线程休眠一段时间。在超过休眠时间之后,线程将会自动被唤醒,不需要其他线程进行通知。当我们的线程睡眠时间太长,我们需要提前唤醒睡眠的线程时,我们可以使用 Thread.interrupt() 方法。此方法表示中断这个线程。在我们的实际运用当中,interrupt() 方法可以实现终止线程的睡眠,提前唤醒线程。
wait() 方法可以传递休眠时间参数,也可以不传递任何参数。不传递任何参数时表示永久休眠,直到另一个线程调用了 notify 或 notifyAll 之后,休眠的线程才能被唤醒。也就是说 sleep 方法具有主动唤醒功能,而不传递任何参数的 wait 方法只能被动的被唤醒。Thread.interrupt() 方法对 wait() 方法依然存在终止线程的睡眠,提前唤醒线程的效果。

区别四:释放锁资源不同

首先给定结论:wait 方法会主动的释放锁,而 sleep 方法则不会。

wait() 方法代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static void lockOfWait() throws Exception {
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println("新线程获取到锁:" + LocalDateTime.now());
try {
lock.wait(2000);
System.out.println("新线程获释放锁:" + LocalDateTime.now());
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();

Thread.sleep(200);
System.out.println("主线程尝试获取锁:" + LocalDateTime.now());
synchronized (lock) {
System.out.println("主线程获取到锁:" + LocalDateTime.now());
}
}

该代码使用了 wait() 用于线程休眠2秒,在另一个主线程种尝试获取锁。如果成功获取到锁,则说明 wait() 在休眠时会释放锁,反之则说明不会释放锁。执行结果如下:

1
2
3
4
5
6
新线程获取到锁:2023-08-11F20:03:49.027
主线程尝试获取锁:2023-08-11F20:03:49.209
主线程获取到锁:2023-08-11F20:03:49.209
新线程获释放锁:2023-08-11F20:03:51.029

Process finished with exit code 0
从上述结果可以看出,当调用了 wait() 之后,主线程立马尝试获取锁成功了,这就说明 wait() 休眠时是释放锁的。

sleep() 方法代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static void lockOfSleep() throws Exception {
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println("新线程获取到锁:" + LocalDateTime.now());
try {
Thread.sleep(2000);
System.out.println("新线程获释放锁:" + LocalDateTime.now());
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();

// 等新线程先获得锁
Thread.sleep(200);
System.out.println("主线程尝试获取锁:" + LocalDateTime.now());
// 在新线程休眠之后,尝试获取锁
synchronized (lock) {
System.out.println("主线程获取到锁:" + LocalDateTime.now());
}
}

该代码使用了 sleep() 用于线程休眠2秒,在另一个主线程种尝试获取锁。如果成功获取到锁,则说明 sleep() 在休眠时会释放锁,反之则说明不会释放锁。执行结果如下:

1
2
3
4
5
6
新线程获取到锁:2023-08-11F20:08:20.139
主线程尝试获取锁:2023-08-11F20:08:20.334
新线程获释放锁:2023-08-11F20:08:22.154
主线程获取到锁:2023-08-11F20:08:22.154

Process finished with exit code 0
从上述结果可以看出,在调用了 sleep() 之后,在主线程里尝试获取锁却没有成功,只有 sleep() 执行完之后释放了锁,主线程才正常的得到了锁,这说明 sleep() 在休眠时并不会释放锁。

区别五:线程进入状态不同

调用 sleep() 方法及有参 wait() 方法线程会进入 TIMED_WAITING 有时限等待状态,而调用无参数的 wait() 方法,线程会进入 WAITING 无时限等待状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private static void getStatus() throws Exception {
Object lock = new Object();
Thread wait = new Thread(() -> {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
wait.start();

Thread waitWithParam = new Thread(() -> {
synchronized (lock) {
try {
lock.wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
waitWithParam.start();

Thread sleep = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
sleep.start();

Thread.sleep(500);
System.out.println("wait() 方法后状态为:" + wait.getState());
System.out.println("wait(millis) 方法后状态为:" + waitWithParam.getState());
System.out.println("sleep(millis) 之后进入状态:" + sleep.getState());
}

代码执行结果如下:

1
2
3
4
wait() 方法后状态为:WAITING
wait(millis) 方法后状态为:TIMED_WAITING
sleep(millis) 之后进入状态:TIMED_WAITING

Sleep() 和 Wait() 比较

安全性:

wait() 安全性较高:wait() 必须在同步块中调用,它会释放锁,等待其他线程通知,因此避免了死锁的风险。它在等待时会放弃对象的锁,允许其他线程进入同步块,然后在被唤醒时重新尝试获取锁。
sleep()安全性较低:sleep()不会释放锁,它只是让线程休眠一段时间。如果在同步块内调用sleep(),其他线程无法进入同步块,可能导致死锁。

常用业务:

wait() 用于线程间通信:wait() 适合用于多个线程之间的通信,其中一个线程等待另一个线程的通知。常用于生产者-消费者模式、多线程协作等场景。
sleep() 用于暂停执行:sleep() 用于暂停当前线程的执行,常用于模拟等待、定时任务等场景。

稳定性:

wait() 稳定性较高:在正确使用的情况下,wait() 能够稳定地实现线程之间的通信,避免了资源竞争和死锁的问题。但需要注意正确处理 InterruptedException。

资源占用:

wait() 会释放对象的锁,允许其他线程进入同步块,从而避免了资源的长时间占用。
sleep() 不会释放锁,因此可能会造成资源的长时间占用,需要谨慎使用。

通知机制:

wait() 需要配合 notify() 或 notifyAll() 来通知等待线程继续执行。这个通知机制允许多个线程等待同一个对象上的通知,非常适合用于线程间的协调和通信。
sleep() 没有内建的通知机制,只是简单的休眠一段时间。不适合多线程协作,通常用于暂停执行或定时任务。

总结

wait() 和 sleep() 是Java多线程编程中的重要方法,虽然它们都能让线程暂停,但用途不同。wait() 常用于线程间通信,释放锁、等待通知,避免死锁,适合协作场景;sleep() 多用于暂停执行,不释放锁,适合模拟等待或定时任务。选择方法时需考虑安全性、业务需求和稳定性。了解这两者差异,能更高效地设计多线程程序,提升应用的性能和可维护性。

最后附上本文所写源代码:Sleep和Wait的的区别、比较与简单使用

  • 标题: Sleep 和 Wait 的区别、比较与简单使用
  • 作者: HYF
  • 创建于 : 2023-08-11 21:26:32
  • 更新于 : 2024-07-27 21:21:52
  • 链接: https://youfeng.ink/sleep&&wait-516dcd7bafcc/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。