生产者消费者模型

一、介绍
(一)、概念:
        如果要实现以下操作流程,使用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、付费专栏及课程。

余额充值