多线程——wait和notify

本文讨论了Java中wait和notify在处理多线程场景中的应用,如ATM取款机模拟,解释了wait的作用、使用条件以及与sleep和notifyAll的区别,展示了如何使用这些方法解决线程间的协作问题以避免死锁。
摘要由CSDN通过智能技术生成

        wait和notify的引入

        试想一个这样的场景,ABC三人去atm取钱,当A进入atm后,就会锁上门,也就是对门加锁,此时BC想要进入atm就要等待A开门,也就是释放锁。但是当A进去后发现,atm里没钱,需要等工作人员往里面补钱,所以他就出来了。但是出来之后,他想要知道有钱了没有,所以又需要再进去。当A出来后,就会和BC一起参与到“门”这个锁的竞争中,但是,在这个竞争中,刚刚释放锁的A是有优势的,也就是大概率A又会重新拿到这个锁。如果一直重复下去,就会导致BC无法正确完成工作,而银行工作人员也无法补充现金,导致这个场景卡死。

        用代码表示大致是这样:

public static void main(String[] args) {
        Thread A = new Thread(() ->{
            while(true){
                synchronized (门){
                    if (有钱) {
                        //取钱
                        break;
                    } else {

                    }
                }
            }
        });
        Thread B = new Thread(() ->{
            while(true){
                synchronized (门){
                    if (有钱) {
                        //取钱
                        break;
                    } else {

                    }
                }
            }
        });
        Thread C = new Thread(() ->{
            while(true){
                synchronized (门){
                    if (有钱) {
                        //取钱
                        break;
                    } else {

                    }
                }
            }
        });
    }

        那如何让A出来后不再进入,而是收到通知后再进入呢?

        这就需要使用到wait和notify了

        wait的使用

        wait的主要作用就是让线程进入等待状态。

        wait是Object类的一个方法,可以选择不传入参数,传入一个参数,传入两个参数

        如果不传入参数,那么线程就会无限等待下去,直至被唤醒然后解除等待状态。

        如果传入参数,那么线程会在时间结束或者被主动唤醒的情况下解除等待状态。   

        使用wait时,wait会进行以下操作:

        使当前执⾏代码的线程进⾏等待.(把线程放到等待队列中)
        释放当前的锁
        满⾜⼀定条件时被唤醒,重新尝试获取这个锁.

        wait的使用一定要配合synchronized来使用,脱离synchronized的wait会直接抛出异常

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

        wait结束的条件:

        其他线程使用notify方法

        等待超时

        其他线程调⽤该等待线程的interrupted⽅法,导致wait抛出InterruptedException异常

        wait使用案例:

public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Thread t = new Thread(() ->{
            synchronized (lock){
                System.out.println("wait之前");
                try {
                    lock.wait(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait之后");
            }
        });
        t.start();
        Thread.sleep(3000);
        System.out.println("3s过去了");
    }

        

        可以看到,进入wait后五秒触发超时,wait结束,线程解除等待,然后正常执行.

        notify的使用

        norify的作用时唤醒正在wait的线程,有以下几个注意事项:

        ⽅法notify()也要在同步⽅法或同步块中调⽤,该⽅法是⽤来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。

        如果有多个线程等待,则有线程调度器随机挑选出⼀个呈wait状态的线程。(并没有"先来后到")

        在notify()⽅法后,当前线程不会⻢上释放该对象锁,要等到执⾏notify()⽅法的线程将程序执⾏完,也就是退出同步代码块之后才会释放对象锁。

        示例,让wait提前唤醒:

public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Thread t1 = new Thread(() ->{
            synchronized (lock){
                System.out.println("wait之前");
                try {
                    lock.wait(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait之后");
            }
        });
        Thread t2 = new Thread(()->{
            synchronized (lock){
                lock.notify();
            }
        });
        t1.start();
        t2.start();
        Thread.sleep(3000);
        System.out.println("3s过去了");
    }

        

        可以看到,由于notify提前唤醒了wait,所以再输出“wait之前”后输出的时“wait之后”再等待3s才输出“3s过去了”.

        notifyAll

        notifyAll的用法基本一致,唯一的区别是:notifyAll会将所有等待的线程一齐唤醒,让他们重新进入锁的竞争。

        解决刚开始的问题

        有了这两个方法,就可以完美的解决这个问题,只需要在A观察到没有钱后,让他进入wait中,等待工作人员的notiy即可,代码可以这样表示:

public static void main(String[] args) {
        Object men = new Object();
        Thread A = new Thread(() ->{
            while(true){
                synchronized (men){
                    if (有钱) {
                        //取钱
                        break;
                    } else {
                        try {
                            men.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
        Thread B = new Thread(() ->{
            while(true){
                synchronized (men){
                    if (有钱) {
                        //取钱
                        break;
                    } else {
                        try {
                            men.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
        Thread C = new Thread(() ->{
            while(true){
                synchronized (men){
                    if (有钱) {
                        //取钱
                        break;
                    } else {
                        try {
                            men.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
        Thread worker = new Thread(()->{
            synchronized (men){
                //补钱
                men.notifyAll();//通知所有人已经补钱
            }
        });
    }

        在worker唤醒所有人后,ABC会重新来竞争“门”这个锁,三个线程谁会拿到这个锁时随机的。

        wait和sleep的区别

        从等待时间上,wait可以无限等,而sleep必须设有一个时间,超过时间将自动唤醒

        从唤醒上,wait是通过notify来通知唤醒,而sleep只能强制interrupt唤醒

        从使用上,wait需要搭配synchronized使⽤,sleep不需要。wait一般用于不知道要等待多久,超时时间更像是一个保险措施来“兜底”,而sleep是知道要等待多久的

        从从属关系上,wait是object的一个方法,而sleep是Thread的一个方法

          

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值