zoukankan      html  css  js  c++  java
  • 线程协作

    线程通信

    应用场景:生产者消费者问题
    • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费;

    • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止;

    • 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。

    分析

    生产者和消费者模式,它不是一种设计模式,而是一种解决由多线程引发的同步问题的办法,也称为有限缓冲问题。这是一个线程同步问题,生产者和消费者共享一个资源,并且生产者和消费者之间相互依赖,互为条件。而解决问题的关键就是,使消费者和生产者同时只能有一个线程访问这块缓存,也就是说它们之间是互斥的。

    • 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又需要马上通知消费者消费;

    • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费;

    • 在生产者消费者问题中,仅有synchronized是不够的

      • synchronized可阻止并发更新同一个共享资源,实现了同步;

      • synchronized不能用来实现不同线程之间的消息传递(通信)。

    Java提供了几个方法解决线程之间的通信问题

    方法名字作用
    wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
    wait(long timeout) 指定等待的毫秒数
    notify() 唤醒一个处于等待状态的线程(同一个对象上)
    notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

    注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常。

    解决方式一:管程法

    并发协作模型:生产者/消费者模式

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

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

    • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个"缓冲区"

    生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据。

    实际应用案例:

    /*
        测试:生产者消费者模型---》利用缓冲区解决:管程法
        生产者、消费者、产品、缓冲区
     */
    public class TestPC_GC {
        public static void main(String[] args) {
            SynContainer container = new SynContainer();
            new Productor(container).start();
            new Consumer(container).start();
        }
    }
    // 生产者,只负责生产
    class Productor extends Thread{
        SynContainer container;
        public Productor(SynContainer container){
            this.container = container;
        }
    ​
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                container.push(new Chicken(i));
                System.out.println("生产了"+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 = 0;
    ​
        // 生产者放入产品
        public synchronized void push(Chicken chicken){
            // 如果容器满了,就需要等待消费者消费
            if (count == chickens.length){
                // 通知消费者消费,生产者等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 如果没有满,就需要丢入产品
            chickens[count] = chicken;
            count++;
    ​
            // 可以通知消费者消费了
            this.notify();
    ​
        }
    ​
        // 消费者消费产品
        public synchronized Chicken pop(){
            // 判断能否消费
            if (count==0){
                // 等待生产者生产,消费者等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 如果可以消费
            count--;
            Chicken chicken = chickens[count];
    ​
            // 吃完了,通知生产者生产
            this.notify();
            return chicken;
        }
    }

    解决方式二:信号灯法

    并发协作模型:生产者消费者模式

    • 设置一个标识位flag;

    • 当标识为真的时候消费者消费,生产者等待;

    • 当标识为假的时候,生产者生产,消费者等待。

    实际的应用案例:

    /*
        测试:生产者消费者模型---》利用缓冲区解决:信号灯法,标志位解决
    ​
     */
    public class TestPC_Singal {
        public static void main(String[] args) {
            TV tv = new TV();
            new Player(tv).start();
            new Watcher(tv).start();
    ​
        }
    }
    ​
    // 生产者-->演员,只负责表演
    class Player extends Thread{
        TV tv;
        public Player(TV tv){
            this.tv = tv;
        }
    ​
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                if (i%2==0){
                    this.tv.play("琅琊榜");
                }else {
                    this.tv.play("伪装者");
                }
            }
        }
    }
    ​
    // 消费者-->观众,只负责观看
    class Watcher extends Thread{
        TV tv;
        public Watcher(TV tv){
            this.tv = tv;
        }
    ​
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                tv.watch();
            }
        }
    }
    ​
    // 产品-->节目
    class TV{
        // 演员表演,观众等待
        // 观众观看,演员等待
        String voice;
        boolean flag = true;
    ​
        // 表演
        public synchronized void play(String voice){
    ​
            if (!flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("演员表演了:"+voice);
            //通知观众观看
            this.notify(); // 通知唤醒
            this.voice = voice;
            this.flag = !this.flag;
        }
        // 观看
        public synchronized void watch(){
            if (flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("观看了:"+this.voice);
            //通知演员表演了
            this.notify();
            this.flag = !this.flag;
        }
    }

    一定要记得生产者和消费者是相互依赖的,而且双方在同一时刻是互斥的,当生产者生产了产品之后要立马通知消费者消费,当消费者消费完一个产品之后,要立马告诉生产者进行生产。

  • 相关阅读:
    01背包问题需要找出相应路径
    单链表的正序输出和逆序输出
    二叉树之叶子节点个数
    01背包问题
    STL之map和multimap(关联容器)
    python的tips:字符和字符串的问题
    postman的使用(转载)
    python tips(3);import的机制
    python每日一类(5):itertools模块
    python每日一类(4):slice
  • 原文地址:https://www.cnblogs.com/yxym2016/p/12578741.html
Copyright © 2011-2022 走看看