生产者和消费者模型是操作系统中经典的同步问题。该问题最早由Dijkstra提出,用以演示它提出的信号量机制。

经典的生产者和消费者模型的描写叙述是:有一群生产者进程在生产产品。并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中。消费者进程可从一个缓冲区中取走产品去消费。虽然全部的生产者进程和消费者进程都是以异步方式执行的。但它们之间必须保持同步,即不同意消费者进程到一个空缓冲区去取产品,也不同意生产者进程向一个已装满产品且尚未被取走的缓冲区投放产品。

首先我们复习一下操作系统中同步机制中应遵循的准则:

  1. 空暇让进:当无进程处于临界区时,应同意一个请求进入临界区的进程进入临界区;
  2. 忙则等待:当已有进程进入临界区时,其它试图进入临界区的进程必须等待。
  3. 有限等待:对要求訪问临界资源的进程,应保证在有限时间内能进入自己的临界区。以免陷入“死等”状态。
  4. 让权等待:当进程不能进入自己的临界区时,应马上释放处理机。以免进程陷入“忙等”;

    在生产者和消费者模型中要保证一下几点:

    1.生产者在往缓存队列中放产品时,消费者不能取产品。

    2.消费者从缓存队列中取产品时。生产者不能放产品。

    3.同一时刻仅仅有一个生产者能够往缓存队列中放产品。

    4.同一时刻仅仅有一个消费者能够从缓存队列中取产品。

    5.缓存队列满时生产者不能往缓存队列中放产品。

    6.缓存队列为空时消费者不能从缓存队列中取产品。

本样例中的缓存队列模仿java jdk中的ArrayBlockingQueue,这是一个堵塞队列,缓存池满时会自己主动将生产者线程挂起,缓存池空时会自己主动将消费者线程挂起。

缓存池

public class Pool<E> {

    /**队列最长长度*/
private int MaxSize = 1000; /**队列默认长度*/
private static final int defaultSize = 100; /**资源池*/
private Object[] objs ; /**队头*/
private int front; /**队尾*/
private int rear; /**元素的个数*/
private int nItems; /** Main lock guarding all access */
final ReentrantLock lock; /** Condition for waiting takes */
private final Condition notEmpty; /** Condition for waiting puts */
private final Condition notFull; private int useSize = 0; public Pool() {
this(defaultSize);
useSize = defaultSize;
} public Pool(int size) {
if(size < 0)
throw new IndexOutOfBoundsException();
size = size > MaxSize ? MaxSize : size;
useSize = size;
objs = new Object[size];
front = 0;
rear = -1;
nItems = 0; lock = new ReentrantLock(true);
notEmpty = lock.newCondition();
notFull = lock.newCondition(); } /**进队*/
private void queue(E e) {
if(rear == useSize - 1)
rear = -1;
objs[++rear] = e;
nItems++;
notEmpty.signal();
} /**出队*/
private E dequeue() {
E e = (E)objs[front++];
if(front == useSize)
front = 0;
nItems--;
notFull.signal();
return e;
} /**进队 资源池满会将入队线程挂起*/
public void offer(E e) throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
while(nItems == objs.length)
notFull.await();
queue(e);
System.out.println("学生进队。当前池中有 " + nItems + " 名同学" );
} finally {
lock.unlock();
} } /**出队 资源池空会将出队线程挂起*/
public E poll() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
while(nItems == 0)
notEmpty.await();
E e = dequeue();
System.out.println("学生出队,当前池中有 " + nItems + " 名同学" );
return e;
} finally {
lock.unlock();
}
} /**是否满*/
public boolean isFull() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return nItems == MaxSize ? true : false;
} finally {
lock.unlock();
} } /**推断是否为空*/
public boolean isEmpty() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return nItems == 0 ? true : false;
} finally {
lock.unlock();
} } /**返回队列中元素个数*/
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return this.nItems;
} finally {
lock.unlock();
} }
}

測试模型

public class Student {

    private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} }

主类

public class PM {

    private Pool<Student> pools = new Pool<Student>(1000);

    public static void main(String[] args) {
PM pm = new PM();
ExecutorService executor = Executors.newFixedThreadPool(6);
executor.execute(pm.new consume());
executor.execute(pm.new consume());
executor.execute(pm.new consume());
executor.execute(pm.new produce());
executor.execute(pm.new produce());
executor.execute(pm.new produce());
} class produce implements Runnable { @Override
public void run() {
while(true) {
try {
pools.offer(new Student());
} catch (InterruptedException e) {
e.printStackTrace();
}
} } } class consume implements Runnable { @Override
public void run() {
while(true) {
try {
pools.poll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} } }

执行结果:

java并发之生产者消费者模型的更多相关文章

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

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

  2. Java里的生产者-消费者模型(Producer and Consumer Pattern in Java)

    生产者-消费者模型是多线程问题里面的经典问题,也是面试的常见问题.有如下几个常见的实现方法: 1. wait()/notify() 2. lock & condition 3. Blockin ...

  3. Java实现多线程生产者消费者模型及优化方案

    生产者-消费者模型是进程间通信的重要内容之一.其原理十分简单,但自己用语言实现往往会出现很多的问题,下面我们用一系列代码来展现在编码中容易出现的问题以及最优解决方案. /* 单生产者.单消费者生产烤鸭 ...

  4. Java并发之:生产者消费者问题

    生产者消费者问题是Java并发中的常见问题之一,在实现时,一般可以考虑使用juc包下的BlockingQueue接口,至于具体使用哪个类,则就需要根据具体的使用场景具体分析了.本文主要实现一个生产者消 ...

  5. java多线程之生产者消费者模型

    public class ThreadCommunication{ public static void main(String[] args) { Queue q = new Queue();//创 ...

  6. Java多线程15:Queue、BlockingQueue以及利用BlockingQueue实现生产者/消费者模型

    Queue是什么 队列,是一种数据结构.除了优先级队列和LIFO队列外,队列都是以FIFO(先进先出)的方式对各个元素进行排序的.无论使用哪种排序方式,队列的头都是调用remove()或poll()移 ...

  7. Java生产者消费者模型

    在Java中线程同步的经典案例,不同线程对同一个对象同时进行多线程操作,为了保持线程安全,数据结果要是我们期望的结果. 生产者-消费者模型可以很好的解释这个现象:对于公共数据data,初始值为0,多个 ...

  8. 如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例

    wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...

  9. Java多线程之~~~使用Exchanger在线程之间交换数据[这个结合多线程并行会有解决很多问题]生产者消费者模型

    http://blog.csdn.net/a352193394/article/details/39503857  Java多线程之~~~使用Exchanger在线程之间交换数据[这个结合多线程并行会 ...

随机推荐

  1. Android全局退出的两种方法

    第一种方法参考<第一行代码>78页 建立一个ActivityCollector类,提供静态方法addActivity,fininshAll(以list为容器) 然后我们建立的Activit ...

  2. Python: PS 滤镜-- Fish lens

    本文实现 PS 滤镜中的一种几何变换– Fish lens, 对图像做扭曲,感觉就像通过一个凸镜或者凹镜在观察图像一样. import numpy as np from skimage import ...

  3. Impala通过JDBC方式访问

    不多说,直接上干货! • 配置: – impala.driver=org.apache.hive.jdbc.HiveDriver – impala.url=jdbc:hive2://node2:210 ...

  4. ElasticSearch 工作原理

    ElasticSearch 工作原理图 文字说明,以后更新

  5. windows下go语言环境

    1  liteIDE ,随便装哪儿都行 2  GO语言包 安装  ,我安装到了 c:\go  (顺便给个地址下载地址  https://golang.org/dl/  ) 3  GCC编译器 安装,同 ...

  6. Kinect 开发 —— ColorBasic

    创建一个Kincet项目通常需要: 1. 创建一个VS项目,一般为了展示通常创建一个wpf项目. 2. 添加Microsoft.Kinect.dll引用,如果是早期版本的SDK,这个名称可能不同. 3 ...

  7. (JavaScript基础向)日常小发现:forEach等函数的第二个参数的用法

    forEach函数用得平时用得比较多,但是从来没想到forEach函数还有第二个参数. 这里是菜鸟教程对forEach函数的详细说明:forEach的详细说明. 如上图,forEach函数有第二个参数 ...

  8. 数据结构(C实现)------- 单链表

    在单链表中,每个结点包括两部分:存放每个数据元素本身信息的数据域和存放其直接后继存储位置的指针域. 单链表结点的类型描写叙述: typedef int ElemType; typedef struct ...

  9. Web前端开发实战4:导航菜单(一)

    在前面的博文中我们提到横向一级菜单,这里我们来看看导航菜单. 导航菜单种类非常多,可是制作原理都是大同 小异的.这里看的比二级下拉式菜单还简单. 来看一些站点上的导航菜单: 垂直导航菜单: 水平导航菜 ...

  10. Eclipse&STS常用小技巧

    开发是经常用到的代码可进行快捷提示,比如mian alt+/就提示出了main方法是不是很方便,为什么就能提示出来呢? 马上来了: 在你使用的java开发工具中点击Window--->Prefe ...