小玉来更新博客了,这次应该文本内容较少,但是看完对玉粉们应该很有帮助,大家耐心看完吧~~~
虽然我们的线程是随机调度的,但是我们在某特定场景 下仍然希望线程按照我们预期的顺序执行,上篇我们了解到join有这样的功能,但是join有个最大的弊端就是,它只能让线程完成整的串行化执行,这样其实不利于多线程并发编程的思维,所以我们还有更多的方法,这里我们先讲wait:
目录
1.wait的用法
*wait需要做的三件事
2.notify 的用法
*notify需要做的三件事
3.线程的六种状态
4.wait与sleep的区别?
wait设计的初心是:当时机不成熟时,让某个线程暂停下来等一等,让出cup以供其他线程调度.
我们举个例子:假如ABCD去餐厅打饭(为了模拟线程随机调度的情况这里不做排队要求),当A到档口前(假设档口被占用模拟上锁情况),发现饭没了.....阿姨告知他等一下,后面正在做,做好了叫你....此时他退出档口,这时候, 档口未上锁,ABCD仍然有同时竞争档口的权利,那大家想一想,假如有一种情况是A来,A去,A又抢到,A又去,A还是抢到,A再次去.......反反复复,只有A在盲目的争夺CPU资源,而其他线程只有等待的份儿,这时候我们需要人为的干预,让ABCD等着,假如饭做好了,我们"唤醒"ABCD,然后再进行档口争夺,打饭.....
有需求就有市场,我们wait应运而生,观察以下代码:
public class ThreadDemo11 {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
System.out.println("wait之前");
obj.wait();
System.out.println("wait之后");
}
}
"非法监视器异常"是什么意思?
这这里我们的监视器意味着synchronized"监视器锁",先来了解wait需要做的三件事:
1.解锁
2.阻塞等待
3.唤醒之后,尝试重新获取锁
那么第一步解锁就意味着一点要在加锁的基础上解锁,不然就抛出异常了,所以我们必须把wait写在synchronized里面,进入synchronized的左大括号意味着加锁,这样才能解锁!
更改代码:
public class ThreadDemo11 {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
System.out.println("wait之前");
synchronized (obj){
obj.wait();
}
System.out.println("wait之后");
}
}
注意!!! synchronized的锁对象必须和里面的wait的调用者是同一个对象!!!!
那么和wait 搭配的notify 意味着"唤醒,苏醒",设计的初心就是唤醒正在等待的处于WAITTING中的线程.
那么同理:notify要想唤醒对应的wait就必须和wait一样,放在synchronized中,并且使用同一个锁对象!!! 另外我们要格外注意:必须先执行wait再执行notify,否则唤醒操作就会空打一炮,wait也不能被正常唤醒,就失去了使用wait¬ify的初心了.程序这时候不会报错,但是会一直处在WAITTING状态,无法按照我们希望的顺序执行.......
观察如下代码:
public class ThreadDemo11 {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread t1 = new Thread(()->{
System.out.println("wait之前");
synchronized (obj){
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("wait之后");
});
t1.start();
Thread.sleep(1000);
Thread t2 = new Thread(()->{
System.out.println("notify之前");
synchronized (obj){
obj.notify();
}
System.out.println("notify之后");
});
t2.start();
}
}
可以看到,我在启动的t1线程的时候,使用wait,然后休眠了1s,确保我的wait能运行到,然后启动t2 线程使用notify唤醒,才能得到理想结果....我们继续深入剖析一下:
大家注意我上面1234中1,3画下划线的部分,二者阻塞原因不同,希望大家认真推敲!!!
- 加wait的时候释放的锁.
- 唤醒wait
- 解锁
另外,还有一个方法是notifyAll,它可以唤醒所有正在wait的线程(是同一对象的前提下).被唤醒的线程继续争夺资源,参与CPU的随机调度.
在一个瞬时时间内,一个线程有且只有一个状态.线程的状态有六种(生命周期)分别是:
我们可以多了解一点关于wait:在使用wait的时候,可以设置参数"最大等待时间"(ms单位) ,这样可以有效避免盲目死等,超出最大等待时间之后,我们就算没有notify唤醒,也可以自动唤醒自己.....
不同点:
1.初心不同:
wait设计的初心是,让部分线程任务在时机不合适的情况下,稍作等待,这样可以合理避免资源盲目争夺CPU.而sleep仅仅是让程序休眠.
2.对锁的操作不同:
wait在触发的时候,会释放当前的锁,基于初心的目的,合理退出CPU的调度,等待唤醒.而sleep在加锁的情况下,不会释放锁,仅仅是休眠.
3.线程的状态不同:
wait时,线程处在WAITING状态;sleep时线程处在TIMED_WAITTING状态.
相同点:
1. 都有带参数的版本:
wait是体现"最大等待时间",超时未有notify唤醒则自动唤醒,而sleep是体现"休眠时间".
2.都可以被提前唤醒,
wait被提前唤醒指的是未设置/未到最大等待时间的前提下,被notify主动唤醒.不抛异常,而sleep被提前换新需要抛出"打断异常".
好了,小玉先讲这么多,写博客不在字数多少,而在于是否质量到位,我会形成一周两篇博客的习惯,请大家监督!