Java多线程-线程协作(生产者消费者模式)管程法和信号灯法

81 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第33天,点击查看活动详情

应用场景:

  1. 假设仓库中只能生产一件商品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
  2. 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
  3. 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止

线程通信分析

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

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

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

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

    1. synchronized可阻止并发更新同一个共享资源,实现了同步
    2. synchronized不能用来实现不同线程之间的消息传递(通信)

线程通信

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

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

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

解决方式 1

并发协作模型,"生产者/消费者模式"-->管程法

  1. 生产者:负责生产数据的模块(可能是方法,对象,线程,进程);
  2. 消费者:负责处理数据的模块(可能是方法,对象,线程,进程);
  3. 缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区

生产者将生产好的数据放到缓存区,消费者从缓存区拿出数据

解决方式 2

并发协作模型 ,"生产者/消费者模式"-->信号灯法

通过boolean标识来进行判断(红路灯)

总之:生产者生产 消费者消费,两条线程之间k可以进行通信,

管程法

并发协作模型,"生产者/消费者模式"-->管程法

  1. 生产者:负责生产数据的模块(可能是方法,对象,线程,进程);
  2. 消费者:负责处理数据的模块(可能是方法,对象,线程,进程);
  3. 缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区

生产者将生产好的数据放到缓存区,消费者从缓存区拿出数据

代码例子



package com.wyh.thread;



 /**

 *  @program:  Thread

 *  @description:  测试:生产者消费者模型  利用缓冲区解决:管程法

 *  @author:  魏一鹤

 *  @createDate:  2022-01-19 23:14

 **/



 //测试:生产者消费者模型  利用缓冲区解决:管程法

 //需要 生产者 消费者 产品 缓冲区

public class TestPC {

    public static void main(String[] args){

        //容器

 SynContainer synContainer = new SynContainer();

         //生产者

  new Productor(synContainer).start();

        //消费者

  new Consumer(synContainer).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++) {

            //循环插入

  try {

                container.push(new Chicken(i));

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            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++) {

            try {

                System.out.println( "消费了" +container.pop().num+ "只鸡" );

            } catch (InterruptedException e) {

                e.printStackTrace();

            }



        }

    }

}

 //产品 鸡

class Chicken{

    //编号

  int num;

    //有参构造

  public Chicken(int num) {

        this.num = num;

    }

}

 //缓冲区

class SynContainer{

    //需要一个容器大小  初始大小为10

 Chicken[] chickens=new Chicken[10];

    //容器计数器

  int count=0;

    //生产者生产产品 同步方法

  public synchronized void push(Chicken chicken) throws InterruptedException {

        //如果容器满了 就需要等待消费者消费

  if(count==chickens.length){

            //容器满了 通知消费者消费,生产等待

  this.wait();

        }

        //如果没有满,就需要丢入产品

  chickens[count] = chicken;

        count++;

        //可以通知消费者消费了

  this.notifyAll();

    }

    //消费者消费产品

  public synchronized Chicken pop() throws InterruptedException {

        //判断能否消费

  if(count==0){

            //没有产品 等待生产者生产,消费者等待

  this.wait();

        }

        //如果可以消费 消费

  count--;

        Chicken chicken=chickens[count];

        //吃完了 通知生产者生产

  this.notifyAll();

        return chicken;

    }

}

信号灯法

并发协作模型 ,"生产者/消费者模式"-->信号灯法

通过boolean标识来进行判断(红路灯)

package com.wyh.thread;



 /**

 *  @program:  Thread

 *  @description:  测试生产者 消费者问题2:信号灯法,标志位解决

 *  @author:  魏一鹤

 *  @createDate:  2022-01-22 18:35

 **/



 //定义boolean表示解决

public class TestPC2 {

    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){

                try {

                    this.tv.play( "快乐大本营" );

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }else{

                try {

                    this.tv.play( "抖音" );

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    }

}

 //消费者 观众

class Watcher extends Thread{

    TV tv;

    //构造方法

  public Watcher(TV tv) {

        this.tv = tv;

    }

    //消费者观看节目

 @Override

    public void run() {

        for (int i = 0; i < 20; i++) {

            try {

                this.tv.watch();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}

 //产品 节目

class TV{

    //演员表演 观众等待 true

 //观众观看 演员等待 false

 String voice; //表演的节目

  boolean flag=true; //等待标识符

 //表演

  public synchronized  void play(String voice) throws InterruptedException {

        if(!flag){

            this.wait(); //等待

 }

        System.out.println( "演员表演了:" + voice);

        //通知观众观看

  this.notifyAll(); //通知唤醒

  this.voice=voice;

        this.flag=!this.flag;

    }

    //观看

  public synchronized  void watch() throws InterruptedException {

        //如果flag为true 那就还没表演

  if(flag){

         this.wait(); //等待

 }

        System.out.println( "观众观看了:" + voice);

        //通知演员表演

  this.notifyAll(); //通知唤醒

  this.flag=!this.flag;

    }



}