线程间的通信---wait()、notify()

1. wait()

作用:让当前线程进入等待状态(使线程停止运行),同时,wait()也会让当前线程释放它所持有的锁,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,当前线程被唤醒(进入“就绪态”)

注意

  • 方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法是用来将当前线程置入“预执行队列”中,并且在wait()所在的代码处停止执行,直到接到通知或被中断为止。
  • wait()方法只能在同步方法中或同步块中调用。如果调用wait()时,没有持有适当的锁,会抛出异常。
  • wait()方法执行后,当前线程释放锁,线程与其它线程竞争重新获取锁。

2. notify()和notifyAll()

作用:唤醒当前对象上的等待线程(让停止的线程继续运行);notify()是随机唤醒一个线程,而notifyAll()是唤醒所有的线程

注意:

  • 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对 其发出通知notify,并使它们重新获取该对象的对象锁。如果有多个线程等待,则有线程规划器随机挑选出一个呈wait状态的线程。
  • 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。

3. wait(long timeout)

作用:让当前线程处于阻塞状态,直到其他线程调用此对象的notify()方法或 notifyAll() 方法, 或者超过指定的时间量,当前线程被唤醒。

注:wait(),notify()必须使用在synchronized同步方法或者代码块内


推荐写法:

for / whie 循环 {
    sychronized(object) {
        while(不满足执行条件时) { //一直执行
        //wait()执行时,到被其他线程notify通知,当前线程被唤醒,
        //竞争到对象锁,恢复继续执行,这段时间内,while中的条件可能已经发生改变
            object.wait();
        }
        //满足执行条件
        执行...业务
        //不推荐写notify(),在极端情况下,可能导致所有线程阻塞等待
        object.notifyAll()
    }
}
  • 在推荐写法中,不建议写notify(),因为在极端情况下,可能导致所有线程阻塞等待

流程图:

在这里插入图片描述

面包店问题:

/**
 * 面包店
 * 10个生产者,每个每次生产3个
 * 20个消费者,每个每次消费1个
 * 库存最大容量为100
 */
public class BreadShop {

    /**
     * 面包店库存
     */
    private static int COUNT;

    /**
     * 消费者
     */

    public static class Consumer implements Runnable{

        private String name;

        public Consumer(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            //一直消费
            try {
                while (true){
                    synchronized (BreadShop.class){
                        //库存到达下限,不能继续消费,阻塞等待
                        if(COUNT == 0){
                            //库存等于0时阻塞等待
                            BreadShop.class.wait();//释放对象锁
                        }else{
                            //库存满足消费条件,允许消费
                            COUNT--;
                            System.out.printf("消费者 %s 消费了1个面包, 库存%s\n", name, COUNT);
                            //通知由BreadShop.class.wait();代码进入阻塞的线程
                            BreadShop.class.notifyAll();
                            //BreadShop.class.notify(); //不建议使用notify
                            //模拟消费的耗时
                            Thread.sleep(200);
                        }
                    }
                    Thread.sleep(100); //优化
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 生产者
     */
    public static class Producer implements Runnable{

        private String name;

        public Producer(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            try {
                //达到生产次数
                while (true){
                    synchronized (BreadShop.class){
                        //库存到达上限,不能继续生产,需要阻塞等待
                        if(COUNT + 3 > 100){
                            BreadShop.class.wait();//释放对象锁
                        }else{
                            COUNT+=3;
                            //库存满足生产条件
                            System.out.printf("生产者 %s 生产了3个面包, 库存%s\n", name, COUNT);
                            //通知BreadShop.class.wait();代码进入阻塞的线程
                            BreadShop.class.notifyAll();
                            //模拟消费的耗时
                            Thread.sleep(200);
                        }
                    }
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        //同时启动20个消费者线程
        Thread[] consumers = new Thread[20];
        for(int i = 0; i < 20; i++){
            consumers[i] = new Thread(new Consumer(String.valueOf(i)));
        }
        //同时启动10个生产者线程
        Thread[] producers = new Thread[10];
        for(int i = 0; i < 10; i++){
            producers[i] = new Thread(new Producer(String.valueOf(i)));
        }

        for(Thread t : consumers){
            t.start();
        }
        for(Thread t : producers){
            t.start();
        }
    }
}

面包店问题进阶:

/**
 * 面包店
 * 10个生产者,每个每次生产3个
 * 20个消费者,每个每次消费1个
 * 升级需求:面包师傅每个最多生产30次:
 *            消费者不再一直消费,把生产者生产完的面包消费完,就结束
 * 隐藏信息:面包店每天生产面包的最大数量:面包店每天生产10*30*3=900个面包
 *         消费者,把900个面包消费完结束
 */
public class BreadShop2 {

    /**
     * 消费者数量
     */
    private static int 消费者数量 = 10;
    /**
     * 每次消费的面包数
     */
    private static int 每次消费的面包数 = 5;
    /**
     * 生产者数量
     */
    private static int 生产者数量 = 5;
    /**
     * 生产者生产次数
     */
    private static int 生产次数 = 10;

    /**
     * 每次生产的面包数
     */
    private static int 每次生产的面包数 = 3;
    /**
     * 最大库存数
     */
    private static int 最大库存数 = 100;

    /**
     * 面包店库存
     */
    private static int 面包店库存;

    /**
     * 面包店生产面包的总数,不会消费
     */
    private static int 生产面包的总数;

    /**
     * 消费者
     */
    public static class Consumer implements Runnable{

        private String name;

        public Consumer(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            //一直消费
            try {
                while (true){
                    synchronized (BreadShop2.class){
                        if(生产面包的总数 == 生产者数量 * 生产次数 * 每次生产的面包数){
                            break;
                        }
                        //库存到达下限,不能继续消费,需要阻塞等待
                        if(面包店库存 - 每次消费的面包数 < 0){
                            BreadShop2.class.wait();//释放对象锁
                        }else {
                            //库存满足消费条件,允许消费
                            面包店库存 -= 每次消费的面包数;
                            System.out.printf("消费者 %s 消费了%s个面包, 库存%s\n", name, 每次消费的面包数, 面包店库存);
                            //通知BreadShop.class.wait();代码进入阻塞的线程
                            BreadShop2.class.notifyAll();
                            //模拟消费的耗时
                            Thread.sleep(200);
                        }
                    }
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //生产者
    public static class Producer implements Runnable{

        private String name;

        public Producer(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            try {
                //达到生产次数
                for (int i = 0; i < 生产次数; i++){
                    synchronized (BreadShop2.class){
                        //库存到达上限,不能继续生产,需要阻塞等待
                        while(面包店库存 + 每次生产的面包数 > 最大库存数){
                            BreadShop2.class.wait();//释放对象锁
                        }
                        面包店库存 += 每次生产的面包数;
                        生产面包的总数 += 每次生产的面包数;
                        //库存满足生产条件
                        System.out.printf("生产者 %s 生产了%s次, 库存%s, 生产的数量%s\n", name, i+1, 面包店库存, 生产面包的总数);
                        //通知BreadShop.class.wait();代码进入阻塞的线程
                        BreadShop2.class.notifyAll();
                        //模拟消费的耗时
                        Thread.sleep(200);
                    }
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        //同时启动消费者线程
        Thread[] consumers = new Thread[消费者数量];
        for(int i = 0; i< 消费者数量; i++){
            consumers[i] = new Thread(new Consumer(String.valueOf(i)));
        }
        //同时启动10个生产者线程
        Thread[] producers = new Thread[生产者数量];
        for(int i = 0; i< 生产者数量; i++){
            producers[i] = new Thread(new Producer(String.valueOf(i)));
        }

        for(Thread t : consumers){
            t.start();
        }
        for(Thread t : producers){
            t.start();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值