多线程|wait、notify的使用

线程执行顺序的随机性的根本原因是随机调度和抢占式执行,但在开发的过程中,我们往往希望代码按照一定的顺序执行,因此Java中提供了一些可以控制线程执行顺序的方法,通过这些方法让线程主动阻塞,让出CPU资源。wait搭配notify使用就可以控制线程的执行顺序。

wait和notify如何使用呢?我们借助代码来说明。

复制代码
public static void main(String[] args) {
        Object object = new Object();
        Thread t1 = new Thread(() ->{
            System.out.println("t1:wait之前");
            try{
                object.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("t1:wait之后");
        });
        Thread t2 = new Thread(() ->{
            System.out.println("t2:notify之前");
            object.notify();
            System.out.println("t2:notify之后");
        });
        t1.start();
        t2.start();
    }
复制代码

梳理上述代码的思路:首先明确wait、notify都是Object类的方法,因此在使用wait和notify的时候,要先创建Object对象再去调用。创建线程t1,调用wait方法,让线程t1进入阻塞,创建线程t2,调用notify方法唤醒线程t1,来看看执行结果:

 

 执行结果出现了非法的锁状态异常,为什么有这个异常?来看看wait的工作原理:

1、释放锁;

2、进行阻塞;

3、收到notify通知后,重新尝试锁,获取锁后,继续往下执行。

wait和notify的使用必须搭配锁来使用,我们在相应位置加上锁:

复制代码
 public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        Thread t1 = new Thread(() -> {
            System.out.println("t1 : wait之前");
            synchronized (object){
                try{
                    object.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("t1 :wait之后");
            }
        });
        Thread t2 = new Thread(() -> {
            System.out.println("t2 : notify之前");
            synchronized (object){
                object.notify();
            }
            System.out.println("t2 :notify之后");

        });
        t1.start();
        t2.start();
    }
复制代码

我们看看执行结果:

 

从结果可以看到,t2线程先t1线程执行,t2线程执行到notify时,会去唤醒wait,但问题是此时线程t1中的wait还未执行到,那么此时notify就是没有作用的,而当线程t1执行到wait时,由于没有notify来唤醒,所以就一直在阻塞等待。当然了,线程的随机调度也有可能先调度执行线程t1,再执行t2,notify就能发挥作用,但是我们想每次执行时都要t1线程先执行,此时的解决办法是在t2.start()之前调用sleep()。

 

 

此时sleep(500)大概率会让t1线程执行到wait的,但是极端情况下,电脑非常卡的情况下,线程的调度时间可能超过500ms,那此时sleep也是没有作用的。我们再来看看最终的代码通常情况下的执行结果:

 

 

我们再次梳理代码的逻辑,创建线程t1,在t1线程中调用wait,创建线程t2,调用notify唤醒线程t1,t1线程被唤醒之后,继续往下执行,也就是打印输出“t1:wait”之后,而上述的执行效果都是基于t1线程先执行的前提下才能实现的,由于线程的随机调度,线程t2有可能先于t1线程执行,执行到notify,若现在线程1还没执行到wait,那么此时notify是没有任何作用的,因此必须得让t1线程先执行到wait,解决方法是在t2.start()之前调用sleep,以保证t1线程执行到wait。

注意:调用wait的对象和锁对象必须是同一个对象,同理notify也是一样的。

 

posted @   司丝思  阅读(141)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示