生产者消费者模型

本文详细介绍并演示了Java中的生产者消费者模型,通过具体案例(蛋糕生产与销售)阐述了线程间的协作机制,包括如何使用wait(), notify() 和 notifyAll() 方法实现线程的等待与唤醒。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、介绍:

(一)、概念:

        如果要实现以下操作流程,使用Java代码来实现:

  1.  多个蛋糕师生产蛋糕,多个消费者购买蛋糕; 
  2.  蛋糕的最大库存为5个; 
  3.  早生产的蛋糕先被销售,最后被生产的蛋糕要最后被售出 

 

        如果要实现这个过程,一定要借助Java线程的并发协作来做。其实这在Java中叫做生产者消费者模型(确切说应该是生产者-消费者-仓储”模型)。

        对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的。就像学习每一门编程语言一样,Hello World!都是最经典的例子。

 

(二)、 生产者消费者模型,应该明确一下几点:

 1、生产者仅仅在仓储未满时候生产,仓满则停止生产。

 2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。

 3、当消费者发现仓储没产品可消费时候会通知生产者生产

 4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费

 

(三)、核心技术:

        Java中提供了三个非常重要方法用来解决“生产者-消费者-仓储”模型 。

  1.  wait():让当前线程进入阻塞状态,将这个线程存储到等待池中,并释放当前线程的所获得的锁 
  2.  notify():唤醒等待池中的一个线程(随机) 
  3.  notifyAll():唤醒等待池中所有的线程

【注意】

  •  wait、notify、notifyAll三个方法必须被在同步代码中被调用 
  •  这些方法都是用于操作线程状态,那么就必须要明确到底操作的是哪个锁上的线程

 

二、代码实现

(一)、实现步骤:

1、定义仓库类:

  • 实现生产消费方法
  • 在仓满和仓空时等待(wait)和唤醒(notify)
  • 使用同步锁

2、定义生产者线程类

  • 定义构造方法,初始化仓库类
  • 在run方法中无限循环执行仓库类的生产方法

3、定义消费者线程类

  • 定义构造方法,初始化仓库类
  • 在run方法中无限循环执行仓库类的消费方法

 

 

(二)、核心代码

 

public class ProducerConsumerGodown {

 

 public static void main(String[] args) throws Exception {

  new ProducerConsumerGodown().startTest();

 }

 public void startTest() {

  Godown godown = new Godown();

  Producer producer1 = new Producer(godown);

  producer1.setName("1号糕点师:");

  producer1.start();

  Producer producer2 = new Producer(godown);

  producer2.setName("2号糕点师:");

  producer2.start();

  Producer producer3 = new Producer(godown);

  producer3.setName("3号糕点师:");

  producer3.start();

  Producer producer4 = new Producer(godown);

  producer4.setName("4号糕点师:");

  producer4.start();

  Producer producer5 = new Producer(godown);

  producer5.setName("5号糕点师:");

  producer5.start();

  Consumer consumer1 = new Consumer(godown);

  consumer1.setName("张三:");

  consumer1.start();

  Consumer consumer2 = new Consumer(godown);

  consumer2.setName("李四:");

  consumer2.start();

  Consumer consumer3 = new Consumer(godown);

  consumer3.setName("王五:");

  consumer3.start();

 }

 class Godown {

  // 定义柜台中的蛋糕

  private LinkedList<Object> storeHouse = new LinkedList<Object>();

  // 定义柜台中最大允许的蛋糕库存量

  private int MAX = 5;

  private Object lock = new Object();

  // 生产蛋糕

  public void produce() {

   try {

    synchronized (lock) {

     // if(storeHouse.size() >=

     // MAX)//注意这里只能用while循环不能用if来判断,if判断只适合1对1的关系,就是只有2个线程,

     // 如果多个线程用if判断的话,就会出现逻辑错误。

     // 所以在多线程中都用while来做判断,不用if

     while (storeHouse.size() == MAX) {

      System.out.println("蛋糕库存已满,糕点师停止生产,等待售出!---库存量:"

        + storeHouse.size());

      lock.wait();

     }

     Object newObj = new Object();

     // 往柜台中添加蛋糕

     if (storeHouse.add(newObj)) {

      System.out.println(Thread.currentThread().getName()

        + "生产入库!---产品编号:" + newObj.hashCode()

        + "---库存  增加  至:" + storeHouse.size());

      // 唤醒在此对象锁上处于等待状态的所有线程

      lock.notifyAll();

     }

    }

    Thread.sleep((long) (Math.random() * 3000));

   } catch (InterruptedException e) {

    System.out.println(e.toString());

   }

  }

  public void consume() {

   try {

    synchronized (lock) {

     // 在多线程中都用while来做判断,不用if

     while (storeHouse.size() == 0) {

      System.out.println("蛋糕已售空,请消费者等待!---库存量:"

        + storeHouse.size());

      lock.wait();

     }

     // 将集合中的第一个元素移除,也就是将最早生产的蛋糕售出

     // LinkedList与ArrayList相比,后者不具有removeFirst方法

     Object object = storeHouse.removeFirst();

     System.out.println(Thread.currentThread().getName()

       + "购买产品!---产品编号:" + object.hashCode()

       + "---库存  减少  至:" + storeHouse.size());

     // 唤醒在此对象锁上处于等待状态的所有线程

     lock.notifyAll();

    }

    Thread.sleep((long) (Math.random() * 3000));

   } catch (InterruptedException e) {

    System.out.println(e.toString());

   }

  }

 }

 class Producer extends Thread {

  private Godown godown;

  public Producer(Godown godown) {

   this.godown = godown;

  }

  public void run() {

   while (true) {

    // 生产

    godown.produce();

   }

  }

 }

 // 定义消费者类

 class Consumer extends Thread {

  private Godown godown;

  public Consumer(Godown godown) {

   this.godown = godown;

  }

  public void run() {

   while (true) {

    // 消费

    godown.consume();

   }

  }

 }

}

 

【附加:】

1、ArrayList   查找速度,原因是ArrayList的底层用得是数组结构;插入和删除,因为需要移动后续的对象;

2、LinkedList    查找速度,原因是迭代循环;插入和删除,原因是只更新引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值