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