在计算机领域中,生产者-消费者问题(也叫bounded-buffer问题)是一类很经典的多进程同步问题。该问题描述了两类进程,即生产者进程和消费者进程,它们共享一个固定大小的缓冲区作为队列。生产者的任务是产生数据,并放到缓冲区中;同时消费者会消费数据(或者说将数据从缓冲区中移走)。每次生产或者消费数据的个数都是一个。在解决该问题时要确保当缓冲区满了之后,不会再向其中添加数据;当缓冲区为空的时候,消费者不能从中提取数据。

当缓冲区满了时,生产者要么进入休眠状态,要么丢弃产生的数据。等到下次消费者从缓冲区中移走一项数据之后,要通知生产者继续往缓冲区中添加数据。同样的,当消费者发现缓冲区为空时,消费者进入休眠状态。当生产者往缓冲区中写入数据之后,生产者要唤醒消费者来消费数据。该问题可以通过进程间通信来解决,典型的做法是使用信号量。如果该问题解决不当会导致死锁,即生产者和消费者都处于等待唤醒的状态。

这里参考了网上的写法,写了一个很简单的版本。有4个文件:

1. 容器,Container.java

2. 生产者,Producer.java

3. 消费者,Consumer.java

4. 测试文件,Test.java

Producer.java

package com.tuhooo.practice.pcmodel;

/**
* 生产者
*/
public class Producer implements Runnable { private Container<Integer> container;
private Object producerMonitor;
private Object consumerMonitor; Producer(Container<Integer> container, Object producerMonitor, Object consumerMonitor) {
this.container = container;
this.producerMonitor = producerMonitor;/*生产者的锁*/
this.consumerMonitor = consumerMonitor;/*消费者的锁*/
} public void run() {
while (true) {
produce();
}
} private void produce() { if (container.isFull()) {
synchronized (producerMonitor) {
try {
if (container.isFull()) {
producerMonitor.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
synchronized (producerMonitor) {
if (!container.isFull()) {
container.add(0);
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("生产者【" + Thread.currentThread().getId() + "】生产了一个, 一共" + container.getSize() + "个");
}
}
} if (!container.isEmpty()) {
synchronized (consumerMonitor) {
if (!container.isEmpty()) {
consumerMonitor.notify();
}
}
}
}
}

Consumer.java

package com.tuhooo.practice.pcmodel;

/**
* 消费者
*/
public class Consumer implements Runnable { private Container<Integer> container;
private Object producerMonitor;
private Object consumerMonitor; Consumer(Container<Integer> container, Object producerMonitor, Object consumerMonitor) {
this.container = container;
this.producerMonitor = producerMonitor;/*生产者的锁*/
this.consumerMonitor = consumerMonitor;/*消费者的锁*/
} public void run() {
while (true) {
consume();
}
} /*消费方法怎么写*/
private void consume() { if (container.isEmpty()) {
//消费者挂起
synchronized (consumerMonitor) {
try {
if (container.isEmpty()) {
consumerMonitor.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
synchronized (consumerMonitor) {
if (!container.isEmpty()) {
container.get();
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("消费者【" + Thread.currentThread().getId() + "】消费了一个, 还有" + container.getSize() + "个");
}
}
} if (!container.isFull()) {
synchronized (producerMonitor) {
if (!container.isFull()) {
producerMonitor.notify();
}
}
}
}
}

Container.java

package com.tuhooo.practice.pcmodel;

import java.util.LinkedList;
import java.util.List; /**
* 容器
*/
public class Container<T> { private int capacity;
private List<T> list; public Container(int capacity) {
this.capacity = capacity;
list = new LinkedList<T>();
} /*往容器中添加*/
public synchronized boolean add(T product) {
if(list.size() < capacity) {
list.add(product);
return true;
}
return false;
} /*从容器中取*/
public synchronized T get() {
if(list.size() > 0) {
list.remove(0);
}
return null;
} /*判断是否是满的*/
public boolean isFull() {
return list.size() >= capacity;
} public boolean isEmpty() {
return list.size() == 0;
} public synchronized int getSize() {
return list.size();
} public int getCapacity() {
return this.capacity;
}
}

Container有点坑的地方就是不知道什么时候应该加synchronized

Test.java

package com.tuhooo.practice.pcmodel;

public class Test {

    public static void main(String[] args) {

        Container<Integer> container = new Container<Integer>(10);
Object producerMonitor = new Object();
Object consumerMonitor = new Object(); Thread p1 = new Thread(new Producer(container, producerMonitor, consumerMonitor));
Thread p2 = new Thread(new Producer(container, producerMonitor, consumerMonitor));
Thread c1 = new Thread(new Consumer(container, producerMonitor, consumerMonitor));
Thread c2 = new Thread(new Consumer(container, producerMonitor, consumerMonitor)); p1.start();
p2.start();
c1.start();
c2.start();
}
}

目前先就这么写,暂时运行起来没啥问题,如果有问题恳请各位看官指出。

有待改进地方:

  1. Container有没有更好的实现方法
  2. Container可以有多个,这样的话可以随机找到一个可用的Container分配给Producer或者Consumer用
  3. 感觉代码中有好多的synchronized,感觉性能不是很好

===========================  更新  ==================

今天再来看这个其实是有问题的,不需要用到两把锁,是因为get和add都是同步方法,那么在执行任意一个的时候dou必须获取对象的锁,其实这两个方法同一时间只有一个可以运行,根本不需要用到两把锁,直接把Container当作锁就可以了。

生产者和消费者问题学习以及Java实现的更多相关文章

  1. “全栈2019”Java多线程第二十五章:生产者与消费者线程详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. Android(java)学习笔记71:生产者和消费者之等待唤醒机制

    1. 首先我们根据梳理我们之前Android(java)学习笔记70中关于生产者和消费者程序思路: 2. 下面我们就要重点介绍这个等待唤醒机制: (1)第一步:还是先通过代码体现出等待唤醒机制 pac ...

  3. Android(java)学习笔记11:生产者和消费者之等待唤醒机制

    1. 首先我们根据梳理我们之前Android(java)学习笔记70中,关于生产者和消费者程序思路: 2. 下面我们就要重点介绍这个等待唤醒机制: (1)第一步:还是先通过代码体现出等待唤醒机制 下面 ...

  4. JAVA学习第二十七课(多线程(六))- 多生产者多消费者问题(JDK1.5新特性)

    多生产者多消费者问题 以生产馒头 消费馒头为例. class Resource { private String name; private int count = 1; private boolea ...

  5. Java线程(学习整理)--4---一个简单的生产者、消费者模型

     1.简单的小例子: 下面这个例子主要观察的是: 一个对象的wait()和notify()使用情况! 当一个对象调用了wait(),那么当前掌握该对象锁标记的线程,就会让出CPU的使用权,转而进入该对 ...

  6. java学习之生产者和消费者案例

    package com.gh.thread; /** * 生产者和消费者案例 * wait和sleep的区别 * wait不让出监视器锁,sleep让出监视器的锁 * @author ganhang ...

  7. JAVA基础再回首(二十五)——Lock锁的使用、死锁问题、多线程生产者和消费者、线程池、匿名内部类使用多线程、定时器、面试题

    JAVA基础再回首(二十五)--Lock锁的使用.死锁问题.多线程生产者和消费者.线程池.匿名内部类使用多线程.定时器.面试题 版权声明:转载必须注明本文转自程序猿杜鹏程的博客:http://blog ...

  8. java生产者与消费者模式

    前言: 生产者和消费者模式是我们在学习多线程中很经典的一个模式,它主要分为生产者和消费者,分别是两个线程, 目录 一:生产者和消费者模式简介 二:生产者和消费者模式的实现 声明:本例来源于java经典 ...

  9. JAVA之旅(十五)——多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止

    JAVA之旅(十五)--多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止 我们接着多线程讲 一.生产者和消费者 什么是生产者和消费者?我们 ...

随机推荐

  1. auto_ptr

    1. int *pi = new int(1024); delete pi; // 释放pi指向的int对象占用的内存空间 pi = 0; // 将pi设置为0,不指向任何东西,为Null 注意:删除 ...

  2. SELECT语句逻辑运行顺序,你知道吗?

    引言 这不是一个什么多深的技术问题.多么牛叉的编程能力.这跟一个人的开发能力也没有很必定的直接关系,可是知道这些会对你的SQL编写,排忧及优化上会有很大的帮助.它不是一个复杂的知识点.可是一个很基础的 ...

  3. C#应用视频教程1.3 Socket通信客户端完善

    我们先把前面的代码封装成一个完整的类,因为跟网络相关的方法并不一定是建立socket的服务器和客户端,所以还是应该把两个分开,比如获取本机IP,修改本机IP,PING远程主机这些事情应该放在一个单独的 ...

  4. C#基础视频教程7.4 如何编写简单游戏

    接下来我们实现整个的游戏流程,当点击开始游戏,则需要三个事情开始运行 1 小鸟初始化并往下掉(当然还是可以用按钮让他飞一下) 2 每隔一定时间从左侧产生一个管子(宽度和高度随机,产生周期2000ms左 ...

  5. 海康ipc onvif抓包分析

    型号:半球DS-2CD2122FWD-IWS 子码流的地址(101+1): rtsp://admin:hik12345@10.7.36.222:554/Streaming/Channels/102?t ...

  6. 算法笔记_035:寻找最小的k个数(Java)

    目录 1 问题描述 2 解决方案 2.1 全部排序法 2.2 部分排序法 2.3 用堆代替数组法 2.4线性选择算法   1 问题描述 有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 2 ...

  7. CentOS 6.2下二进制安装 MySQL 5.6

    在CentOS 6.2上 二进制方式安装 MySQL5.6二进制包 可以参考这篇文章: http://www.cnblogs.com/xiaoit/p/3988640.html 一路配置下来很顺利.. ...

  8. 理解Load Average做好压力测试(转)

    转载自:http://www.blogjava.net/cenwenchu/archive/2008/06/30/211712.html SIP的第四期结束了,因为控制策略的丰富,早先的的压力测试结果 ...

  9. 偏最小二乘回归(PLSR)- 2 标准算法(NIPALS)

    1 NIPALS 算法 Step1:对原始数据X和Y进行中心化,得到X0和Y0.从Y0中选择一列作为u1,一般选择方差最大的那一列. 注:这是为了后面计算方便,如计算协方差时,对于标准化后的数据,其样 ...

  10. 关于RDS备份文件使用wget下载提示403 Forbidden的情况

    关于RDS备份文件使用wget下载提示403 Forbidden的情况 使用wget下载提示403错误当我们RDS物理备份文件时,例如: 原因: URL中包含有特殊字符比如&,从而造成URL被 ...