A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© psuugdufnm 中级黑马   /  2016-4-12 13:28  /  883 人查看  /  6 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文



一、介绍
(一)、概念:
        如果要实现以下操作流程,使用Java代码来实现:
  • 多个蛋糕师生产蛋糕,多个消费者购买蛋糕;
  • 蛋糕的最大库存为5个;
  • 早生产的蛋糕先被销售,最后被生产的蛋糕要最后被售出



        如果要实现这个过程,一定要借助Java线程的并发协作来做。其实这在Java中叫做生产者消费者模型(确切说应该是生产者-消费者-仓储”模型)。
        对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的。就像学习每一门编程语言一样,Hello World!都是最经典的例子。

(二)、 生产者消费者模型,应该明确一下几点:
1、生产者仅仅在仓储未满时候生产,仓满则停止生产。
2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。
3、当消费者发现仓储没产品可消费时候会通知生产者生产
4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费


(三)、核心技术:
        Java中提供了三个非常重要方法用来解决“生产者-消费者-仓储”模型 。
  • wait():让当前线程进入阻塞状态,将这个线程存储到等待池中,并释放当前线程的所获得的锁
  • notify():唤醒等待池中的一个线程(随机
  • notifyAll():唤醒等待池中所有的线程
【注意】
  • wait、notify、notifyAll三个方法必须被在同步代码中被调用
  • 这些方法都是用于操作线程状态,那么就必须要明确到底操作的是哪个锁上的线程





6 个回复

倒序浏览
看不懂呀。。。。
回复 使用道具 举报
程序呢,,让我们看看啊
回复 使用道具 举报
现在没人用同步了。你试试用lock 、condition 更简单。
回复 使用道具 举报
二、代码实现
(一)、实现步骤:
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    查找速度慢,原因是迭代循环;插入和删除快,原因是只更新引用。
回复 使用道具 举报
回复 使用道具 举报
每天积累分。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马