wait和notify

wait和notify都是Object类下的方法

作用:控制线程执行顺序

关于wait


wait的作用:

1.释放当前线程的锁对象

2.使此线程进入阻塞队列

3.满足一定条件时被唤醒,此时会重新尝试获取锁对象


wait必须搭配synchronized使用,不然会抛出异常

  public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        obj.wait();
    }

根据上方wait的功能我们可以分析为何wait和synchronized必须成对使用

wait的作用之一是释放锁,你现在连锁都没有,何来的释放,这不就矛盾了吗,故异常


wait阻塞线程

public static void main(String[] args) {
        Object obj = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (obj){
                System.out.println("wait之前");
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("wait之后");
            }
        });
        t1.start();
    }

wait操作后,线程发生阻塞,阻塞在synchronized代码块中,此时释放锁对象obj,这个时候其他线程是可以获取锁对象obj的


关于notify


notify方法也需要和synchronized一起使用(同步方法或者同步代码块)

 public static void main(String[] args) {
        Object obj = new Object();
        obj.notify();
    }

作用:

1.唤醒现在正在等待(执行了wait方法的线程)了的线程,使他们可以重新尝试获取锁对象

2.如果改锁对象有多个线程都在等待,那么notify方法会随机唤醒一只wait的线程(并没有先来后到啥啥啥的规则顺序)

3.当线程执行notify方法后,并不会立刻改锁对象,而是执行完此线程的所有代码后再释放


wait和notify


1.感受wait和notify

 public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (obj){
                System.out.println("3秒后唤醒obj");
                try {
                    Thread.sleep(3000);
                    obj.notify();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (obj){
                System.out.println("wait之前");
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("wait之后");
            }
        });
        t2.start();
        t1.start();
    }

执行流程:t2先执行拿到obj锁对象,执行wait方法后释放锁对象,线程进入阻塞队列,这个时候t1线程可以获取obj锁对象,休眠3秒后执行notify方法,当t1线程执行完后,释放锁对象,这时候t2线程可以重新获取锁对象,继续执行完下面的代码


理解notify线程执行流程与释放锁的关系

public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        Thread t1 = new Thread(() -> {
        System.out.println("我没在syn代码块内噢");
            for(int i = 0; i < 5; i++){
                synchronized (obj){
                    System.out.println("啦啦啦");
                    obj.notify();
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (obj){
                System.out.println("wait之前");
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("wait之后");
            }
        });
        t2.start();
        t1.start();
    }

线程将notify方法执行后,并不是立马释放对象锁,使此线程立即进入阻塞队列

也不是只执行synchronized代码块内的内容,而是将整个线程中的程序全部跑完后再释放锁对象,这是需要注意的

反之,我们也要注意,t1线程中的System.out.println("我没在syn代码块内噢");并没有在synchronized代码块中,所以它是可以执行的,但由于线程调度的不确定性,我们无法确定是t2先执行还是t1先执行故就有下面的两种结果打印,同时也就引出了下面的问题


有没有可能出现先notify再wait的情况?

如上方所说的,由于线程的不确定性调度,我们无法保证一定是先执行wait,后执行notify,那么如果先执行了notify了会有上面影响呢?

答案:这时notify就是一个无效通知 但是也没啥负面影响

故为了先让wait先执行,我们必须要人为加点手段,让代码先执行wait,再执行notify,这时候代码才是有意义的

        t2.start();
        Thread.sleep(1000);
        t1.start();

wait死等

在wait没有参数的版本下,如果没有线程执行notify方法,那么执行wait操作的线程就会一直等下去,这并不是一个靠谱的选择,万一哪一天我们忘记写notify了,那不就一直这样白白等着了

wait带参版本:指定了最大等待时间wait(long timeout)   单位:毫秒

public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        Thread t = new Thread(() -> {
            synchronized (obj){
                System.out.println("我最多等3秒");
                try {
                    obj.wait(3000);//单位是毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        long begin = System.currentTimeMillis();
        t.start();
        t.join();
        long end  = System.currentTimeMillis();
        System.out.println("等待了"+ (end - begin));
    }


前面我们说过,当线程在sleep时执行interrupt方法,sleep内部会抛出一个异常,此时会唤醒线程,且程序的后续如何执行由程序的代码决定

而wait带参数的版本遇到sleep时,所发生的情况也与之类似

public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        Thread t = new Thread(() -> {
            synchronized (obj){
                System.out.println("我最多等3秒");
                    try {
                        obj.wait(3000);//单位是毫秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        System.out.println("接下里你想干什么,由自己的代码决定");
                    }
                }
        });
        t.start();
        t.interrupt();
    }


notifyAll

notify是随机唤醒一个线程,而notifyAll是唤醒所有等待的线程,一起竞争锁对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值