看完立刻搞懂--wait和notify

        小玉来更新博客了,这次应该文本内容较少,但是看完对玉粉们应该很有帮助,大家耐心看完吧~~~

        虽然我们的线程是随机调度的,但是我们在某特定场景 下仍然希望线程按照我们预期的顺序执行,上篇我们了解到join有这样的功能,但是join有个最大的弊端就是,它只能让线程完成整的串行化执行,这样其实不利于多线程并发编程的思维,所以我们还有更多的方法,这里我们先讲wait:

看完立刻搞懂--wait和notify_第1张图片


目录

1.wait的用法 

*wait需要做的三件事

2.notify 的用法

*notify需要做的三件事 

3.线程的六种状态

4.wait与sleep的区别? 


1.wait的用法 

       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之后");
    }
}

观察结果:
看完立刻搞懂--wait和notify_第2张图片

"非法监视器异常"是什么意思?
这这里我们的监视器意味着synchronized"监视器锁",先来了解wait需要做的三件事: 

*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的调用者是同一个对象!!!! 

2.notify 的用法

        那么和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();
    }
}

观察结果:
看完立刻搞懂--wait和notify_第3张图片

可以看到,我在启动的t1线程的时候,使用wait,然后休眠了1s,确保我的wait能运行到,然后启动t2 线程使用notify唤醒,才能得到理想结果....我们继续深入剖析一下:

  1. 我们先加上了obj 对应的锁,然后在锁内部使用了wait,此时线程因为使用wait阻塞,然后放弃obj锁.
  2.  休眠1s.
  3. 执行到t2,重新对obj加锁,使用notify唤醒操作,(此时wait被唤醒,但是仍需阻塞一小会,因为这里需要尝试重新对obj加锁,但此时是在等待t2线程的notify释放obj锁)  然后t2解锁.
  4. 此时wait不再阻塞,继续执行t1后序任务.

大家注意我上面1234中1,3画下划线的部分,二者阻塞原因不同,希望大家认真推敲!!! 

*notify需要做的三件事 

  1. 加wait的时候释放的锁.
  2. 唤醒wait
  3. 解锁 

另外,还有一个方法是notifyAll,它可以唤醒所有正在wait的线程(是同一对象的前提下).被唤醒的线程继续争夺资源,参与CPU的随机调度. 

3.线程的六种状态

在一个瞬时时间内,一个线程有且只有一个状态.线程的状态有六种(生命周期)分别是:

  1. 新建:NEW  线程被构建,但是还没有调用start()方法
  2. 就绪:RUNNABLE   线程调用start()方法
  3. 阻塞:BLOCKING  阻塞状态,线程无法获取锁对象
  4. 无限期等待:WAITTING  等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)
  5. 限期等待:TIMED_WAITTING  超市等待,线程可在一定时间内自行返回
  6. 终止:TERMINATED  线程死亡,退出

4.wait与sleep的区别? 

        我们可以多了解一点关于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被提前换新需要抛出"打断异常".


        好了,小玉先讲这么多,写博客不在字数多少,而在于是否质量到位,我会形成一周两篇博客的习惯,请大家监督!

看完立刻搞懂--wait和notify_第4张图片

你可能感兴趣的:(java,开发语言)