sleep是Thread类的静态方法,而wait是Object类的成员方法
请看如下代码:创建两个线程t1、t2,t1获得锁后睡眠5s
@Slf4j
public class TestSleepAndWait {
static final String lock = "abc";
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
synchronized (lock){
log.error("t1线程执行");
try {
//
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1");
Thread t2 = new Thread(()->{
// t2获得锁后执行
synchronized (lock){
log.error("t2线程执行");
}
},"t1");
t1.start();
t2.start();
}
}
执行结果:
16:51:01 [t1] gzhu.study.test.TestSleepAndWait - t1线程执行
16:51:06 [t1] gzhu.study.test.TestSleepAndWait - t2线程执行
由日志时间可以看到t2线程执行时间刚好在5s之后,说明t1睡眠过程中,t2一直在阻塞等待t1释放锁。
接下来我们尝试把sleep方法换成wait方法,上述其它代码不变
// t1线程等待
lock.wait();
查看运行结果:
16:57:55 [t1] gzhu.study.test.TestSleepAndWait - t1线程执行
16:57:55 [t1] gzhu.study.test.TestSleepAndWait - t2线程执行
可以看到t1、t2线程几乎在同一时间运行,说明wait方法使线程释放了说资源。值得注意的是,wait方法需要配合锁对象使用,即使用锁对象使当前线程阻塞。
sleep方法调用后,线程进入TIME_WAITING状态,而wait方法调用后,线程会进入WAITING状态。
额外了解一下:什么是等待池?什么是锁池?
等待池就是:线程调用wait方法后,线程会进入一个LIFO(为什么是LIFO下面解释)等待被唤醒,在等待池的线程被唤醒之前是不会去竞争锁资源的。
锁池:存放竞争锁资源的线程,等待池的线程被notify()和notifyAll()方法唤醒之后,会先进入锁池而不是直接获得锁资源执行。另外有一个误区,笔者看到许多博客说sleep方法需要指定时间,而wait方法不需要指定时间其实是错的,如下:
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException
第一种情况:
@Slf4j
public class TestSleepAndWait {
static final String lock = "abc";
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
synchronized (lock){
log.error("t1线程执行");
try {
// t1线程等待
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.error("t1线程继续执行");
}
},"t1");
Thread t2 = new Thread(()->{
// t2获得锁后执行
synchronized (lock){
log.error("t2线程执行");
}
},"t1");
Thread t3 = new Thread(()->{
// t2获得锁后执行
synchronized (lock){
log.error("t3线程执行");
}
},"t3");
t1.start();
t2.start();
t3.start();
}
}
结果:
17:33:03 [t1] gzhu.study.test.TestSleepAndWait - t1线程执行
17:33:03 [t3] gzhu.study.test.TestSleepAndWait - t3线程执行
17:33:03 [t1] gzhu.study.test.TestSleepAndWait - t2线程执行
由于没有唤醒t1线程,即使t2、t3线程释放锁资源后,t1线程也没有继续执行。
第二种情况:
Thread t1 = new Thread(()->{
synchronized (lock){
log.error("t1线程执行");
try {
// t1线程等待
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.error("t1线程继续执行");
}
},"t1");
Thread t2 = new Thread(()->{
// t2获得锁后执行
synchronized (lock){
log.error("t2线程执行");
lock.notify();
while (true){
}
}
},"t1");
t1.start();
t2.start();
运行结果:
17:38:23 [t1] gzhu.study.test.TestSleepAndWait - t1线程执行
17:38:23 [t1] gzhu.study.test.TestSleepAndWait - t2线程执行
即使t1被t2唤醒进入锁池,但是由于t2一直循环未释放锁资源,使得t1也不能立即继续执行。
notify()方法与notifyAll()方法
notifAll方法,是将等待池中所有线程唤醒,并按照LIFO原则唤醒
notify方法是从等待队列中随机唤醒,如果只有一个线程则唤醒一个线程。
原文:https://www.cnblogs.com/yuanfeii/p/14842239.html