在计算机领域中,生产者-消费者问题(也叫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. 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理

    本文为线程本地存储TLS系列之分类和原理. 一.TLS简述和分类 我们知道在一个进程中,所有线程是共享同一个地址空间的.所以,如果一个变量是全局的或者是静态的,那么所有线程访问的是同一份,如果某一个线 ...

  2. SpringSecurity实现后台管理员登录(二)

    需求:login.ftl页面中登录成功后进入index.ftl页面中 一.pom.xml中添加json转换相关的包 <dependency> <groupId>com.fast ...

  3. 【云计算】OpenStack qcow2镜像如何转化为Docker镜像?

    Import qcow2 image to docker hub 参考资料: https://forums.docker.com/t/import-qcow2-image-to-docker-hub/ ...

  4. linux shell的执行方式

    ./ ping.sh 这个意思 ,'./'的意思是在当前目录执行, ping.sh----------------------------------------------------------- ...

  5. 关于使用jquery时,ie8下提示对象不支持的属性或方法的解决办法

    转自:http://wapapp.baidu.com/auoong/item/538790fcbe87c834d7ff8cde 首先这个问题的前提是已经排除了常见的这个问题.下面说一种今天我碰到的一种 ...

  6. 2014年七月最佳jQuery插件荟萃

    本月的jQuery插件荟萃我们将介绍几款非常不错的jQuery插件,涵盖了表单,幻灯,页面设计等等方面,相信大家肯定会喜欢! Select or Die 一款帮助开发者美化并且强化选择框的jQuery ...

  7. mybatis模糊查询语句

    articletitle like concat('%',#{articletitle},'%')

  8. ThinkPHP框架返回插入记录的id号

    ThinkPHP返回插入记录的id号 $Form->create()) $result = $Form->add(); 在执行上述语句后,若存在auto_increment字段,则可以使用 ...

  9. windows 磁盘加密

      windows 磁盘加密 CreateTime--2018年4月25日18:37:45 Author:Marydon 以win10为例 选中磁盘-->你会发现上面的管理BitLocker是置 ...

  10. 关于AWS的备份策略

    AWS有一个很强大的功能,就是snapshot,翻译过来就是对EBS进行快照.通俗的说,即是对整个硬盘进行完整的镜像备份.如此一来,在其中一台EC2挂掉的时候,我们迅速的另起一台EC2,并将通过快照恢 ...