在计算机领域中,生产者-消费者问题(也叫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. 本周推荐7款CSS3实现的动态特效

    大家都知道,在网页制作时使用CSS技术,可以有效地对页面的布局.字体.颜色.背景和其它效果实现更加精确的控制.只要对相应的代码做一些简单的修改,就可以改变同一页面的不同部分,或者页数不同的网页的外观和 ...

  2. Minimum edit distance(levenshtein distance)(最小编辑距离)初探

    最小编辑距离的定义:编辑距离(Edit Distance),又称Levenshtein距离.是指两个字串之间,由一个转成还有一个所需的最少编辑操作次数.许可的编辑操作包含将一个字符替换成还有一个字符. ...

  3. poj 2406 Power Strings KMP匹配

    对于数组s[0~n-1],计算next[0~n](多计算一位). 考虑next[n],如果t=n-next[n],如果n%t==0,则t就是问题的解,否则解为1. 这样考虑: 比方字符串"a ...

  4. js判断是否为数组

      js判断是否为数组类型 CreateTime--2018年5月18日14:38:58 Author:Marydon 1.错误方式 使用typeof 返回的是object 2.正确方式 方式一:使用 ...

  5. chrome 此网页正试图从未经验证的来源加载脚本

      chrome 此网页正试图从未经验证的来源加载脚本 CreateTime--2018年5月25日08点02分 Author:Marydon 1.情景还原 以我的网站为例,https://www.c ...

  6. How vacuum template0

    [pg@h1 ~]$ vacuumdb --freeze template0 vacuumdb: could not connect to database template0: FATAL: dat ...

  7. Eclipse在线安装hibernate tools插件

    今天在看hibernate的视频,当讲到eclipse安装hibernate插件时,视频中的方法是安装本地的一个文件,可我电脑里并没有,于是找网上的在线安装方法,发现有两种,下面介绍这两种——> ...

  8. nginx配置用户认证

    location ~ .*admin\.php$ {             auth_basic "weifenglinux auth";             auth_ba ...

  9. Linux命令-目录处理命令:pwd

    pwd 显示当前目录的绝对路径

  10. Python 列表 min() 方法

    描述 Python 列表 min() 方法返回列表元素中的最小值. 语法 min() 方法语法: min(L) 参数 L -- 要返回最小值的列表. 返回值 返回列表元素中的最小值. 实例 以下实例展 ...