• Java多线程之等待唤醒机制


    1 等待唤醒机制

    1.1 线程间通信

    概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。

    • 为何要处理线程之间的通讯?

    ​ 让多线程在访问同一份资源时按照一定的规律进行。

    • 如何保证线程间通信有效利用资源:

    ​ 多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作,避免对同一共享变量的争夺————等待唤醒机制

    1.2 等待唤醒机制

    等待唤醒机制

    • 是多个线程间的一种协作机制
    • 在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。
    • wait/notify 就是线程间的一种协作机制。

    等待唤醒中的方法

    • wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
    • wait(long timeout):等待指定的毫秒数
    • notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位后,等候就餐最久的顾客最先入座。
    • notifyAll:则释放所通知对象的 wait set 上的全部线程,优先级别高的线程优先调度。

    注意:

    哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调用 wait 方法之后的地方恢复执行。

    总结如下:

    • 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;
    • 否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态

    调用wait和notify方法需要注意的细节

    • wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
    • wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
    • wait方法与notify方法必须要在同步代码块或者是同步函数中使用,否则会抛出异常IIlegalMonitorStateException。因为:必须要通过锁对象调用这2个方法。

    1.3 生产者与消费者问题

    等待唤醒机制其实就是经典的“生产者与消费者”的问题。

    生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件

    • 对于生产者,没有生产产品之前,要通知消费者等待。而生产产品之后,又要马上通知消费者消费
    • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费

    解决方法:线程同步+线程通讯

    1.3.1 信号灯法(通过标志位)

    • 包子铺线程生产包子,吃货线程消费包子。当包子没有时(包子状态为false),吃货线程等待,包子铺线程生产包子(即包子状态为true),并通知吃货线程(解除吃货的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。接下来,吃货线程能否进一步执行则取决于锁的获取情况。如果吃货获取到锁,那么就执行吃包子动作,包子吃完(包子状态为false),并通知包子铺线程(解除包子铺的等待状态),吃货线程进入等待。包子铺线程能否进一步执行则取决于锁的获取情况。
    //资源
    public class Baozi {
        private String pier;
        private String xianer;
        private boolean flag = false;//包子资源,是否存在
        public Baozi() {
        }
        public Baozi(String pier, String xianer) {
            this.pier = pier;
            this.xianer = xianer;
        }
        public String getPier() {
            return pier;
        }
        public void setPier(String pier) {
            this.pier = pier;
        }
        public String getXianer() {
            return xianer;
        }
        public void setXianer(String xianer) {
            this.xianer = xianer;
        }
        public boolean isFlag() {
            return flag;
        }
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    }
    //消费者
    public class ChiHuo extends Thread {
        private Baozi bz;
        public ChiHuo(String name, Baozi bz) {
            super(name);
            this.bz = bz;
        }
        @Override
        public void run() {
            while (true) {
                synchronized (bz) {
                    if (bz.isFlag() == false) {
                        try {
                            bz.wait();//吃货等待
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("吃货正在吃" + bz.getPier() + bz.getXianer() + "包子!");
                    System.out.println("包子吃完了!");
                    bz.setFlag(false);
                    bz.notify();//唤醒包子铺
                }
            }
        }
    }
    //生产者
    public class BaoZiPu extends Thread {
        private Baozi bz;
        public BaoZiPu(String name, Baozi bz) {
            super(name);
            this.bz = bz;
        }
        @Override
        public void run() {
            int count = 0;
            while (true) {
                synchronized (bz) {
                    if (bz.isFlag()) {
                        try {
                            bz.wait();//包子铺停止生产
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("包子铺开始做包子");
                    if (count % 2 == 0) {
                        bz.setPier("冰皮");
                        bz.setXianer("五仁");
                    } else {
                        bz.setPier("薄皮");
                        bz.setXianer("牛肉大葱");
                    }
                    count++;
                    bz.setFlag(true);
                    System.out.println("包子造好了:" + bz.getPier() + bz.getXianer());
                    System.out.println("吃货来吃包子吧");
                    bz.notify();//唤醒吃货吃包子
                }
            }
        }
    }
    public class BaoZiTest {
        public static void main(String[] args) {
            Baozi bz = new Baozi();
            BaoZiPu bzp = new BaoZiPu("包子铺", bz);
            ChiHuo ch = new ChiHuo("吃货", bz);
            bzp.start();
            ch.start();
        }
    }
    /*
    包子铺开始做包子
    包子造好了:冰皮五仁
    吃货来吃包子吧
    吃货正在吃冰皮五仁包子!
    包子吃完了!
    包子铺开始做包子
    包子造好了:薄皮牛肉大葱
    吃货来吃包子吧
    吃货正在吃薄皮牛肉大葱包子!
    包子吃完了!
    包子铺开始做包子
    */
    

    1.3.2 管程法

    • 生产者:负责生产数据的模块(可能是方法、对象、线程、进程)

    • 消费者:负责处理数据的模块(可能是方法、对象、线程、进程)

    • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”,生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

    //生产者、消费者、产品、容器
    public class PCThread {
        public static void main(String[] args) {
            SynContainer container = new SynContainer();
    
            new Provider(container).start();
            new Consumer(container).start();
        }
    }
    
    //生产者
    class Provider extends Thread {
        SynContainer container;
    
        public Provider(SynContainer container) {
            this.container = container;
        }
    
        @Override
        public void run() {
            for (int i = 1; i <= 100; i++) {
                System.out.println("生产者生产第" + i + "只鸡");
                container.push(new Chicken(i));
            }
        }
    }
    
    //消费者
    class Consumer extends Thread {
        SynContainer container;
    
        public Consumer(SynContainer container) {
            this.container = container;
        }
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("消费者消费第" + container.pop().id + "只鸡");
            }
    
        }
    }
    
    //资源 鸡
    class Chicken {
        int id;
    
        public Chicken(int id) {
            this.id = id;
        }
    }
    
    //缓冲区 容器
    class SynContainer {
        //容器大小
        Chicken[] chickens = new Chicken[10];
        //容器计数器
        int count;
    
        //生产者放入产品
        public synchronized void push(Chicken chicken) {
            //容器满了,生产者停止生产,等待消费者消费
            if (count == chickens.length) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //没满,则放入产品
            chickens[count] = chicken;
            count++;
            //通知消费者消费
            this.notifyAll();
        }
    
        //消费者消费产品
        public synchronized Chicken pop() {
            //判断能否消费
            if (count == 0) {
                //消费者等待生产者生产
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //可以消费
            count--;
            Chicken chicken = chickens[count];
    
            //吃完了通知生产者生产
            this.notifyAll();
            return chicken;
        }
     }
    
  • 相关阅读:
    socket阻塞与非阻塞,同步与异步
    Python列表切成多个/生成多个空列表
    virtualbox 下windows与虚拟机实现文件共享---挂载
    centos安装mysql
    centos安装Python2.7
    在遍历或者迭代过程中对集合进行增删 都会发生异常(迭代器的并发异常)
    List
    LinkedList
    增强for循环
    Collection中的迭代器
  • 原文地址:https://www.cnblogs.com/wuweibincqu/p/14139309.html
Copyright © 2020-2023  润新知