一、概念

  生产者与消费者问题是一个金典的多线程协作的问题.生产者负责生产产品,并将产品存放到仓库;消费者从仓库中获取产品并消费。当仓库满时,生产者必须停止生产,直到仓库有位置存放产品;当仓库空时,消费者必须停止消费,直到仓库中有产品。

  解决生产者/消费者问题主要用到如下几个技术:1.用线程模拟生产者,在run方法中不断地往仓库中存放产品。2.用线程模拟消费者,在run方法中不断地从仓库中获取产品。3

.仓库类保存产品,当产品数量为0时,调用wait方法,使得当前消费者线程进入等待状态,当有新产品存入时,调用notify方法,唤醒等待的消费者线程。当仓库满时,调用wait方法,使得当前生产者线程进入等待状态,当有消费者获取产品时,调用notify方法,唤醒等待的生产者线程。

二、实例

  

package book.thread.product;

public class Consumer extends Thread{
  private Warehouse warehouse;//消费者获取产品的仓库
  private boolean running = false;//是否需要结束线程的标志位
  public Consumer(Warehouse warehouse,String name){
    super(name);
    this.warehouse = warehouse;
  }
  public void start(){
    this.running = true;
    super.start();
  }
  public void run(){
    Product product;
    try {
      while(running){
        //从仓库中获取产品
        product = warehouse.getProduct();
        sleep(500);
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  //停止消费者线程
  public void stopConsumer(){
    synchronized(warehouse){
      this.running = false;
      warehouse.notifyAll();//通知等待仓库的线程
    }
  }
  //消费者线程是否在运行
  public boolean isRunning(){
    return running;
  }
}

package book.thread.product;

public class Producer extends Thread{
    private Warehouse warehouse;//生产者存储产品的仓库
  private static int produceName = 0;//产品的名字
  private boolean running = false;//是否需要结束线程的标志位

  public Producer(Warehouse warehouse,String name){
    super(name);
    this.warehouse = warehouse;
  }
  public void start(){
    this.running = true;
    super.start();
  }
  public void run(){
    Product product;
    //生产并存储产品
    try {
    while(running){
      product = new Product((++produceName)+"");
      this.warehouse.storageProduct(product);
      sleep(300);
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  //停止生产者线程
  public void stopProducer(){
    synchronized(warehouse){
      this.running = false;
      //通知等待仓库的线程
      warehouse.notifyAll();
    }
  }
  //生产者线程是否在运行
  public boolean isRunning(){
    return running;
  }
}

package book.thread.product;

public class Product {
  private String name;//产品名
  public Product(String name){
    this.name = name;
  }
  public String toString(){
    return "Product-"+name;
  }
}

package book.thread.product;

//产品的仓库类,内部采用数组来表示循环队列,以存放产品
public class Warehouse {
  private static int CAPACITY = 11;//仓库的容量
  private Product[] products;//仓库里的产品
  //[front,rear]区间的产品未被消费
  private int front = 0;//当前仓库中第一个未被消费的产品的下标
  private int rear = 0;//仓库中最后一个未被消费的产品下标加1
  public Warehouse(){
    this.products = new Product[CAPACITY];
  }
  public Warehouse(int capacity){
    this();
    if(capacity > 0){
      CAPACITY = capacity +1;
      this.products = new Product[CAPACITY];
    }
  }

  //从仓库获取一个产品
  public Product getProduct() throws InterruptedException{
    synchronized(this){
      boolean consumerRunning = true;//标志消费者线程是否还在运行
      Thread currentThread = Thread.currentThread();//获取当前线程
      if(currentThread instanceof Consumer){
        consumerRunning = ((Consumer)currentThread).isRunning();
      }else{
        return null;//非消费者不能获取产品
      }
      //若消费者线程在运行中,但仓库中没有产品了,则消费者线程继续等待
      while((front==rear) && consumerRunning){
        wait();
        consumerRunning = ((Consumer)currentThread).isRunning();
      }
      //如果消费者线程已经停止运行,则退出该方法,取消获取产品
      if(!consumerRunning){
        return null;
      }
      //获取当前未被消费的第一个产品
      Product product = products[front];
      System.out.println("Consumer[" + currentThread.getName()+"] getProduct:"+product);
      //将当前未被消费产品的下标后移一位,如果到了数组末尾,则移动到首部
      front = (front+1+CAPACITY)%CAPACITY;
      System.out.println("仓库中还没有被消费的产品数量:"+(rear+CAPACITY-front)%CAPACITY);
      //通知其他等待线程
      notify();
      return product;
    }
  }
  //向仓库存储一个产品
  public void storageProduct(Product product) throws InterruptedException{
  synchronized(this){
    boolean producerRunning = true;//标志生产者线程是否在运行
    Thread currentThread = Thread.currentThread();
    if(currentThread instanceof Producer){
      producerRunning = ((Producer)currentThread).isRunning();
    }else{
      return;
    }
    //如果最后一个未被消费的产品与第一个未被消费的产品的下标紧挨着,则说明没有存储空间了。
    //如果没有存储空间了,而生产者线程还在运行,则生产者线程等待仓库释放产品
    while(((rear+1)%CAPACITY == front) && producerRunning){
      wait();
      producerRunning = ((Producer)currentThread).isRunning();
    }
    //如果生产线程已经停止运行了,则停止产品的存储
    if(!producerRunning){
      return;
    }
    //保存产品到仓库
    products[rear] = product;
    System.out.println("Producer[" + Thread.currentThread().getName()+"] storageProduct:" + product);
    //将rear下标循环后移一位
    rear = (rear + 1)%CAPACITY;
    System.out.println("仓库中还没有被消费的产品数量:"+(rear + CAPACITY -front)%CAPACITY);
    notify();
    }
  }
}

package book.thread.product;

public class TestProduct {
  public static void main(String[] args) {
    Warehouse warehouse = new Warehouse(10);//建立一个仓库,容量为10
    //建立生产者线程和消费者
    Producer producers1 = new Producer(warehouse,"producer-1");
    Producer producers2 = new Producer(warehouse,"producer-2");
    Producer producers3 = new Producer(warehouse,"producer-3");
    Consumer consumer1 = new Consumer(warehouse,"consumer-1");
    Consumer consumer2 = new Consumer(warehouse,"consumer-2");
    Consumer consumer3 = new Consumer(warehouse,"consumer-3");
    Consumer consumer4 = new Consumer(warehouse,"consumer-4");
    //启动生产者线程和消费者线程
    producers1.start();
    producers2.start();
    consumer1.start();
    producers3.start();
    consumer2.start();
    consumer3.start();
    consumer4.start();
    //让生产者/消费者程序运行1600ms
    try {
      Thread.sleep(1600);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    //停止消费者线程
    producers1.stopProducer();
    consumer1.stopConsumer();
    producers2.stopProducer();
    consumer2.stopConsumer();
    producers3.stopProducer();
    consumer3.stopConsumer();
    consumer4.stopConsumer();
  }
}

输出结果:

Producer[producer-1] storageProduct:Product-1
仓库中还没有被消费的产品数量:1
Consumer[consumer-2] getProduct:Product-1
仓库中还没有被消费的产品数量:0
Producer[producer-3] storageProduct:Product-3
仓库中还没有被消费的产品数量:1
Producer[producer-2] storageProduct:Product-2
仓库中还没有被消费的产品数量:2
Consumer[consumer-3] getProduct:Product-3
仓库中还没有被消费的产品数量:1
Consumer[consumer-1] getProduct:Product-2
仓库中还没有被消费的产品数量:0
Producer[producer-1] storageProduct:Product-4
仓库中还没有被消费的产品数量:1
Consumer[consumer-4] getProduct:Product-4
仓库中还没有被消费的产品数量:0
Producer[producer-3] storageProduct:Product-6
仓库中还没有被消费的产品数量:1
Producer[producer-2] storageProduct:Product-5
仓库中还没有被消费的产品数量:2
Consumer[consumer-1] getProduct:Product-6
仓库中还没有被消费的产品数量:1
Consumer[consumer-2] getProduct:Product-5
仓库中还没有被消费的产品数量:0
Producer[producer-1] storageProduct:Product-7
仓库中还没有被消费的产品数量:1
Consumer[consumer-3] getProduct:Product-7
仓库中还没有被消费的产品数量:0
Producer[producer-3] storageProduct:Product-8
仓库中还没有被消费的产品数量:1
Producer[producer-2] storageProduct:Product-9
仓库中还没有被消费的产品数量:2
Consumer[consumer-4] getProduct:Product-8
仓库中还没有被消费的产品数量:1
Producer[producer-1] storageProduct:Product-10
仓库中还没有被消费的产品数量:2
Producer[producer-3] storageProduct:Product-11
仓库中还没有被消费的产品数量:3
Producer[producer-2] storageProduct:Product-12
仓库中还没有被消费的产品数量:4
Consumer[consumer-1] getProduct:Product-9
仓库中还没有被消费的产品数量:3
Consumer[consumer-2] getProduct:Product-10
仓库中还没有被消费的产品数量:2
Consumer[consumer-3] getProduct:Product-11
仓库中还没有被消费的产品数量:1
Producer[producer-3] storageProduct:Product-13
仓库中还没有被消费的产品数量:2
Producer[producer-1] storageProduct:Product-14
仓库中还没有被消费的产品数量:3
Producer[producer-2] storageProduct:Product-15
仓库中还没有被消费的产品数量:4
Consumer[consumer-4] getProduct:Product-12
仓库中还没有被消费的产品数量:3
Consumer[consumer-1] getProduct:Product-13
仓库中还没有被消费的产品数量:2
Consumer[consumer-2] getProduct:Product-14
仓库中还没有被消费的产品数量:1
Producer[producer-1] storageProduct:Product-16
仓库中还没有被消费的产品数量:2
Producer[producer-3] storageProduct:Product-17
仓库中还没有被消费的产品数量:3
Producer[producer-2] storageProduct:Product-18
仓库中还没有被消费的产品数量:4

分析:在main方法中建立了一个产品仓库,并未该仓库关联了3个生产者线程和4个消费者线程,启动这些线程,使生产 者/消费者模型运作起来,当程序运行1600ms时,所有的生产者停止生产产品,消费者停止消费产品。

  生产者线程Product在run方法中没300ms便生产一个产品,并存入仓库;消费者线程Consumer在run方法中没500ms便从仓库中取一个产品。

  仓库类Warehouse负责存放产品和发放产品。storageProduct方法负责存储产品,当仓库满时,当前线程进入等待状态,即如果生产者线程A在调用storageProduct方法以存储产品时,发现仓库已满,无法存储时,便会进入等待状态。当存储产品成功时,调用notify方法,唤醒等待的消费者线程。

  getProduct方法负责提前产品,当仓库空时,当前线程进入等待状态,即如果消费者线程B在调用getProduct方法以获取产品时,发现仓库空了,便会进入等待状态。当提取产品成功时,调用notify方法,唤醒等待的生产者线程。

java线程中生产者与消费者的问题的更多相关文章

  1. 第23章 java线程通信——生产者/消费者模型案例

    第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...

  2. java线程中的sleep/wait/notify/yield/interrupt方法 整理

    java线程中的sleep/wait/notify/yield/interrupt方法 sleep 该方法能够使当前线程休眠一段时间 休眠期间,不释放锁 休眠时间结束之后,进入可执行状态,加入到线程就 ...

  3. java线程中的sleep和wait区别

                                                                            面试题:java线程中sleep和wait的区别以及其资 ...

  4. 在Java 线程中返回值的用法

    http://icgemu.iteye.com/blog/467848 在Java 线程中返回值的用法 博客分类: Java Javathread  有时在执行线程中需要在线程中返回一个值:常规中我们 ...

  5. Java中生产者与消费者模式

    生产者消费者模式 首先来了解什么是生产者消费者模式.该模式也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例.该问题描述了两个共享固定大小缓冲区的线 ...

  6. JAVA笔记14__多线程共享数据(同步)/ 线程死锁 / 生产者与消费者应用案例 / 线程池

    /** * 多线程共享数据 * 线程同步:多个线程在同一个时间段只能有一个线程执行其指定代码,其他线程要等待此线程完成之后才可以继续执行. * 多线程共享数据的安全问题,使用同步解决. * 线程同步两 ...

  7. Java线程中yield与join方法的区别

    长期以来,多线程问题颇为受到面试官的青睐.虽然我个人认为我们当中很少有人能真正获得机会开发复杂的多线程应用(在过去的七年中,我得到了一个机会),但是理解多线程对增加你的信心很有用.之前,我讨论了一个w ...

  8. Disruptor框架中生产者、消费者的各种复杂依赖场景下的使用总结

    版权声明:原创作品,谢绝转载!否则将追究法律责任. Disruptor是一个优秀的并发框架,可以实现单个或多个生产者生产消息,单个或多个消费者消息,且消费者之间可以存在消费消息的依赖关系.网上其他博客 ...

  9. Python-多线程及生产者与消费者

    一.前置知识 1. 队列基础 如果不指定队列是什么,请自行查阅 在Python中,队列是最常用的线程间的通信方法,因为它是线程安全的 from queue import Queue # 创建队列 # ...

随机推荐

  1. weblogic启动时日志重定向(nohup.out)

    由于weblogic使用  nohup ./startWebLogic.sh &   启动时会将所有日志打印到nohup.out上,长此以往会导致该文件越来越大,不便于管理. 故下面介绍如何重 ...

  2. aix 扩展文件系统

    今天发现公司的oracle测试 数据库不能启动,检查警告日志日志,提示归档空间不足,不能归档,于是扩展文件系统: 1.检查rootvg卷组的剩余空间[p2704u]:[/dsg/oracle11]$ ...

  3. oralce health monitor

    1. Health Monitor简介    Health Monitor是11g里新增加的特性,用于数据库的各层和各个组建的诊断检查.例如可以检查:文件损坏.物理逻辑块损坏.redo和undo故障. ...

  4. sqlplus 连接数据库报错SP2-0642: SQL*Plus internal error state 2130, context 0:0:0

    sqlplus 连接数据库报错SP2-0642: SQL*Plus internal error state 2130, context 0:0:0 问题描述: 使用sqlplus客户端登录数据库,报 ...

  5. 学习Visitor Pattern 有感而发!override and overload

    通过阅读各位前辈写的博文,像吕震宇,idior,李建忠WebCast等,对Visitor模式有一定的了解,有感而记录下来,以备忘. Visitor Pattern 假设了这样一个场景,在一个类型层次中 ...

  6. 用document.title=“xxx”动态修改title,在ios的微信下面不生效

    单页应用里整个页面只会在第一次完全刷新,后面只会局部刷新(一般不包括head及里面的title),所以无法在服务器端控制title,只能在页面刷新的时候通过js修改title.常规做法如下,可惜在iO ...

  7. SQLite 入门教程(一)基本控制台(终端)命令 (转)

    转于: SQLite 入门教程(一)基本控制台(终端)命令 一.基本简介   SQLite 是一个自持的(self-contained).无服务器的.零配置的.事务型的关系型数据库引擎.因为他很小,所 ...

  8. 【面试虐菜】—— MongoDB知识整理

    为什么我们要使用MongoDB? 特点: 高性能.易部署.易使用,存储数据非常方便.主要功能特性有: 面向集合存储,易存储对象类型的数据. 模式自由. 支持动态查询. 支持完全索引,包含内部对象. 支 ...

  9. 百亿级别数据量,又需要秒级响应的案例,需要什么系统支持呢?下面介绍下大数据实时分析工具Yonghong Z-Suite

    Yonghong Z-Suite 除了提供优秀的前端BI工具之外,Yonghong Z-Suite让用户可以选购分布式数据集市来支持实时大数据分析. 对于这种百亿级的大数据案例,Yonghong Z- ...

  10. html传參中?和&

    <a href="MealServlet?type=findbyid&mid=<%=m1.getMealId()%> 在这句传參中?之后的代表要传递的參数当中有两个 ...