生产者和消费者问题学习以及Java实现
在计算机领域中,生产者-消费者问题(也叫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();
}
}
目前先就这么写,暂时运行起来没啥问题,如果有问题恳请各位看官指出。
有待改进地方:
- Container有没有更好的实现方法
- Container可以有多个,这样的话可以随机找到一个可用的Container分配给Producer或者Consumer用
- 感觉代码中有好多的synchronized,感觉性能不是很好
=========================== 更新 ==================
今天再来看这个其实是有问题的,不需要用到两把锁,是因为get和add都是同步方法,那么在执行任意一个的时候dou必须获取对象的锁,其实这两个方法同一时间只有一个可以运行,根本不需要用到两把锁,直接把Container当作锁就可以了。
生产者和消费者问题学习以及Java实现的更多相关文章
- “全栈2019”Java多线程第二十五章:生产者与消费者线程详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Android(java)学习笔记71:生产者和消费者之等待唤醒机制
1. 首先我们根据梳理我们之前Android(java)学习笔记70中关于生产者和消费者程序思路: 2. 下面我们就要重点介绍这个等待唤醒机制: (1)第一步:还是先通过代码体现出等待唤醒机制 pac ...
- Android(java)学习笔记11:生产者和消费者之等待唤醒机制
1. 首先我们根据梳理我们之前Android(java)学习笔记70中,关于生产者和消费者程序思路: 2. 下面我们就要重点介绍这个等待唤醒机制: (1)第一步:还是先通过代码体现出等待唤醒机制 下面 ...
- JAVA学习第二十七课(多线程(六))- 多生产者多消费者问题(JDK1.5新特性)
多生产者多消费者问题 以生产馒头 消费馒头为例. class Resource { private String name; private int count = 1; private boolea ...
- Java线程(学习整理)--4---一个简单的生产者、消费者模型
1.简单的小例子: 下面这个例子主要观察的是: 一个对象的wait()和notify()使用情况! 当一个对象调用了wait(),那么当前掌握该对象锁标记的线程,就会让出CPU的使用权,转而进入该对 ...
- java学习之生产者和消费者案例
package com.gh.thread; /** * 生产者和消费者案例 * wait和sleep的区别 * wait不让出监视器锁,sleep让出监视器的锁 * @author ganhang ...
- JAVA基础再回首(二十五)——Lock锁的使用、死锁问题、多线程生产者和消费者、线程池、匿名内部类使用多线程、定时器、面试题
JAVA基础再回首(二十五)--Lock锁的使用.死锁问题.多线程生产者和消费者.线程池.匿名内部类使用多线程.定时器.面试题 版权声明:转载必须注明本文转自程序猿杜鹏程的博客:http://blog ...
- java生产者与消费者模式
前言: 生产者和消费者模式是我们在学习多线程中很经典的一个模式,它主要分为生产者和消费者,分别是两个线程, 目录 一:生产者和消费者模式简介 二:生产者和消费者模式的实现 声明:本例来源于java经典 ...
- JAVA之旅(十五)——多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止
JAVA之旅(十五)--多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止 我们接着多线程讲 一.生产者和消费者 什么是生产者和消费者?我们 ...
随机推荐
- C++如何禁止掉对象的复制操作
最容易想到的是将拷贝构造函数与赋值函数声明为private.但是,private只是说外部不能直接调用,但是可以间接通过类的成员函数与友元函数对其访问.那么怎么办呢? ---->在类中,允许声明 ...
- CentOS7 常用命令集合
CentOS7 常用命令集合 文件与目录操作 touch test.txt: 创建一个文本文件 文本内容处理 查询操作 压缩.解压 yum安装器 网络相关 系统相关 XSheel 5相关操作 窗体快捷 ...
- 【云计算】Docker 镜像如何设置语言环境?bash: warning: setlocale: LC_ALL: cannot change locale (en_US)
解决方案: # set default language environment RUN locale-gen en_US.UTF- \ && dpkg-reconfigure loc ...
- CentOS7 rpm方式安装mysql5.7
内容源自:CentOS7安装mysql 在CentOS中默认安装有MariaDB,这个是MySQL的分支,但为了需要,还是要在系统中安装MySQL,而且安装完成之后可以直接覆盖掉MariaDB. 1. ...
- Activiti 流程启动及节点流转源代码分析
作者:jiankunking 出处:http://blog.csdn.net/jiankunking 本文主要是以activiti-study中的xiaomage.xml流程图为例进行跟踪分析 详细的 ...
- 自己定义View步骤
概述 Android已经为我们提供了大量的View供我们使用,可是可能有时候这些组件不能满足我们的需求,这时候就须要自己定义控件了.自己定义控件对于刚開始学习的人总是感觉是一种复杂的技术. 由于 ...
- Cocos2d-x -- 如何让背景从上到下滚动
1. 首先,声明一个2个大小的sprite数组 class GameScreen : public cocos2d::Layer { public: ... cocos2d::Sprite *back ...
- QtGui.QCheckBox
A QtGui.QCheckBox is a widget that has two states: on and off. It is a box with a label. Check boxes ...
- synchronized探究
synchronized的加锁方式 synchronized的本质是给对象上锁,对象包括实例对象,也包括类对象.常见的加锁方式有下面几种写法:(1)在非static方法上加synchronized,例 ...
- 百度地图API拾取坐标网址
http://api.map.baidu.com/lbsapi/getpoint/index.html