sleep()
和 wait()
两个方法都是用来暂停线程,具备同样的功能,但是他们之间还是存在蛮大的区别的。
区别一:所属类不同
sleep()
是Thread
类的一个静态方法。位于Thread
中,强调了它是线程中的一部分,代表了线程的一种行为方式,不涉及线程间的交互。wait()
是Object
类的一个方法。在 Java 中一切皆对象,而所有对象都可以作为一个监视器(monitor),将wait()
放在Object
类中,体现的是面向对象设计的原则,是一种线程间通过共享对象进行通信的机制。
区别二:使用语法不同
sleep()
是 Thread
类的一个静态方法,可以随时随地用。而 wait()
必须配合 synchronized 一起使用,不然在运行时就会抛出 IllegalMonitorStateException 的异常,如下:
public class WaitTest {
public static void main(String[] args) throws InterruptedException {
Thread.sleep(1000);
System.out.println("执行 sleep(1000) 后...");
Object lock = new Object();
lock.wait();
}
}
编译是不会有问题的,但是执行就会报错:
这个报错的意思是,一个线程在没有持有对象监视器(monitor) 的情况下就调用了该对象的 wait()
, notify()
, 或 notifyAll()
。所以,一个线程必须要是对象监视器的持有者才能调用这些方法。
区别三:释放锁资源不同
sleep()
会让出 CPU 时间,让其他线程有机会执行,但是它不会释放锁资源。
public class SleepTest {
public synchronized void test() {
System.out.println(Thread.currentThread().getName() + "-获取锁,time:" + LocalTime.now());
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "-休眠完成,释放锁,time:" + LocalTime.now());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
SleepTest sleepTest = new SleepTest();
new Thread(sleepTest::test).start();
new Thread(sleepTest::test).start();
}
}
结果:
线程 Thread-0
和 线程 Thread-1 竞争同一个锁资源 SleepTest 对象。当 Thread-0
进入休眠后,Thread-1
并没有获取到锁资源进入同步方法,而是等 Thread-0
被唤醒后释放锁资源才进入同步方法的。所以,sleep()
暂时时并没有释放锁资源。
- 当线程调用
wait()
时,它会释放它所持有的锁,允许其他线程进入同步代码块或同步方法。
public class WaitTest {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "-成功获取锁,time:" + LocalTime.now());
long i = 1;
while (i < 9999999999L) {
i++;
}
try {
System.out.println(Thread.currentThread().getName() + "-业务执行完成,调用 wait() ,time:" + LocalTime.now());
lock.wait(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
// 等待线程启动获取锁资源
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + "-尝试获取锁,time:" + LocalTime.now());
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "-成功获取锁,time:" + LocalTime.now());
}
}
}
执行结果
Thread-0
在16:53:05
成功获取锁,这时main
在 16:53:05
开始获取锁,由于锁已经被 Thread-0
获取了,所以它一直都在那里等待,在 16:53:10
时Thread-0
终于执行业务完成,调用 wait()
等待 2
秒,而这时 main
成功获取锁,如果 wait()
不释放锁的话,main
获取锁的时间应该是 16:53:12
。
区别四:唤醒方式不同
调用 sleep()
我们需要传入一个参数,这个参数就表示该线程的休眠时间,当休眠时间一到,它会自动被唤醒。而 wait()
被调用后,它会进入对象的等待集(_WaitSet
),它会无限期地等待,直到其他线程调用 notify()
或 notifyAll()
时被唤醒,注意调用 notify()
时,它是随机唤醒线程的,所以调用notify()
后,它还不一定会被唤醒。
当然 wait()
也有一个重载的方法 wait(long timeout)
,这个方法允许我们传入一个时间单位。
区别五:线程状态不一样
直接看示例:
public class WaitTest {
public static void main(String[] args) throws InterruptedException {
Object lock1 = new Object();
Object lock2 = new Object();
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
thread1.start();
Thread thread2 = new Thread(() -> {
synchronized (lock1) {
try {
lock1.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread2.start();
Thread thread3 = new Thread(() -> {
synchronized (lock2) {
try {
lock2.wait(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread3.start();
Thread.sleep(1000);
System.out.println("sleep(3000) 之后进入状态:" + thread1.getState());
System.out.println("wait() 之后进入状态:" + thread2.getState());
System.out.println("wait(3000) 之后进入状态:" + thread3.getState());
}
}
结果
- 调用
sleep()
后,线程状态为:TIMED_WAITING
- 调用
wait()
后,线程状态为:WAITING
- 调用
wait(3000)
后,线程状态为:TIMED_WAITING
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。