【Java】线程暂停比拼:wait() 和 sleep()的较量
欢迎浏览高耳机的博客
希望我们彼此都有更好的收获
感谢三连支持!
在Java多线程编程中,合理地控制线程的执行是至关重要的。wait()
和sleep()
是两个常用的方法,它们都可以用来暂停线程的执行,但它们之间存在着显著的差异。本文将详细探讨这两个方法的区别,并提供一个实际的编程示例来展示如何使用它们来控制线程的执行顺序。
当你被罚站时,需要等待解除罚站的指令才能离开;
而当你睡觉时,到了一定时间自然就会醒来;
wait()
与sleep()
概述
wait()
定义:wait()
是Object
类的一个方法,它使得调用它的线程释放对象的锁,并进入对象的等待池(wait set)。
用途:主要用于线程间的协作,特别是当一个线程需要等待另一个线程完成某些操作时。
锁状态:调用wait()
的线程必须拥有对象的锁,并且在调用后会释放这个锁。
sleep()
定义:sleep()
是Thread
类的一个静态方法,它使得调用它的线程暂停执行指定的时间。
用途:通常用于控制线程的执行时间,或者在不释放锁的情况下让其他线程有机会执行。
锁状态:调用sleep()
的线程不会释放任何锁。
wait()
必须在同步方法或块中调用。
sleep()
可以在任何地方调用。
区别详解
锁状态和对象监视器
wait()
需要在同步方法或块中调用,因为它依赖于对象的锁。调用后,线程会释放锁并进入等待状态。
sleep()
可以在任何地方调用,不需要在同步方法或块中,且不会释放任何锁。
响应中断
在wait()
和sleep()
期间,如果线程被中断,都会抛出InterruptedException
异常。
用途
wait()
通常用于线程间的协作,如生产者-消费者问题。
sleep()
通常用于控制线程的执行时间,或者在不释放锁的情况下让其他线程有机会执行。
返回到执行状态
wait()
在被唤醒后,需要重新获得对象的锁才能继续执行。
sleep()
在睡眠时间结束后自动继续执行,不需要重新获得锁。
实际编程示例
以下是一个使用wait()
和sleep()
来控制线程执行顺序的示例:
有三个线程,线程名称分别为:a,b,c。 每个线程打印自己的名称。 需要让他们同时启动,并按 c,b,a的顺序打印
使用 wait() 实现:
package Thread;
public class Demo11 {
private static Object locker = new Object();
private static int state = 1;
public static void main(String[] args) throws InterruptedException {
//通过state这个变量作为判断条件,不满足该条件的线程进入等待,满足条件的线程则先打印,
//然后改变条件,通过notify唤醒其他线程,继续判断,打印;
Thread thread1 = new Thread(() -> {
synchronized (locker) {
while(state != 1) {
try {
locker.wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("C");
state = 2;
locker.notify();
}
});
Thread thread2 = new Thread(() -> {
synchronized (locker) {
while(state != 2) {
try {
locker.wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("B");
state = 3;
locker.notify();
}
});
Thread thread3 = new Thread(() -> {
synchronized (locker) {
while(state != 3) {
try {
locker.wait();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("A");
state = 1;
locker.notify();
}
});
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
}
}
在这个示例中,我们使用了一个共享对象locker
作为锁,并通过state
变量来控制线程的执行顺序。每个线程在执行前都会检查state
变量,如果不符合条件,就会调用wait()
方法等待。当一个线程执行完毕后,它会更新state
变量并调用notify()
方法来唤醒其他等待的线程。
使用 sleep() 实现
package Thread;
public class Demo13 {
//sleep版
// 有三个线程,线程名称分别为:a,b,c。
// 每个线程打印自己的名称。
// 需要让他们同时启动,并按 c,b,a的顺序打印
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(200);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("C");
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(400);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B");
});
Thread thread3 = new Thread(() -> {
try {
Thread.sleep(600);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A");
});
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
}
}
在这个实例中,我们不需要使用synchronized锁来帮助我们控制进程,而是单纯通过每个线程的睡眠时间不同,利用时间差来实现规定的打印顺序;
这种方法虽然更加直观,但请注意,这种方法并不是线程同步的推荐做法,因为它依赖于硬编码的延迟,这可能会导致不可预测的行为,特别是在不同的系统或不同的负载条件下。通常,使用wait
和notify
或ReentrantLock
和Condition
来实现线程间的协调是更可靠和可控的方法。
结论
理解wait()
和sleep()
的区别对于编写高效的多线程程序至关重要。wait()
和sleep()
都可以用来暂停线程的执行,但它们在锁状态、用途、中断响应等方面有着显著的不同。正确使用这些方法可以帮助你更好地控制线程的执行顺序和协作。