java 多线程并发系列之 生产者消费者模式的两种实现
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
这个阻塞队列就是用来给生产者和消费者解耦的。纵观大多数设计模式,都会找一个第三者出来进行解耦,如工厂模式的第三者是工厂类,模板模式的第三者是模板类。在学习一些设计模式的过程中,如果先找到这个模式的第三者,能帮助我们快速熟悉一个设计模式。
真实世界中的生产者消费者模式
生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系。比如一个人正在准备食物(生产者),而另一个人正在吃(消费者),他们使用一个共用的桌子用于放置盘子和取走盘子,生产者准备食物,如果桌子上已经满了就等待,消费者(那个吃的)等待如果桌子空了的话。这里桌子就是一个共享的对象。
生产者与消费者模型中,要保证以下几点:
1 同一时间内只能有一个生产者生产
2 同一时间内只能有一个消费者消费
3 生产者生产的同时消费者不能消费
4 消费者消费的同时生产者不能生产
5 共享空间空时消费者不能继续消费
6 共享空间满时生产者不能继续生产
/** 生产者与消费者模型中,要保证以下几点:
* 1 同一时间内只能有一个生产者生产
* 2 同一时间内只能有一个消费者消费
* 3 生产者生产的同时消费者不能消费
* 4 消费者消费的同时生产者不能生产
* 5 共享空间空时消费者不能继续消费
* 6 共享空间满时生产者不能继续生产
*
* */ //容器 解耦生产者和消费者
class Box {
private int apple = 0;
private int capacity = 0;
public Box(int n) {
this.capacity = n;
}
public synchronized void increace() {
while (apple == capacity) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
apple++;
System.out.println("生产了一个");
notify();
}
public synchronized void decreace() {
while (apple == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
apple--;
System.out.println("消费了一个");
notify();
}
} //生产者(定义十次):
class Producer implements Runnable {
private Box box; public Producer(Box box) {
this.box = box;
} @Override
public void run() {
for( int i=0;i<10;i++)
{
box.increace();
}
}
} //消费者(同样十次):
class Consumer implements Runnable {
private Box box; public Consumer(Box box) {
this.box = box;
}
@Override
public void run() {
for( int i=0;i<10;i++)
{
box.decreace();
}
}
} //测试
public class StaticTest{
public static void main(String []args)
{
Box box= new Box(5); Thread t1= new Thread(new Producer(box));
Thread t2= new Thread(new Consumer(box)); t1.start();
t2.start(); }
}
生产了一个
生产了一个
生产了一个
生产了一个
消费了一个
消费了一个
消费了一个
消费了一个
生产了一个
生产了一个
生产了一个
生产了一个
生产了一个
消费了一个
消费了一个
消费了一个
消费了一个
消费了一个
生产了一个
消费了一个
阻塞队列实现生产者消费者模式超级简单,它提供开箱即用支持阻塞的方法put()和take(),开发者不需要写困惑的wait-nofity代码去实现通信。BlockingQueue 一个接口,Java5提供了不同的现实,如ArrayBlockingQueue和LinkedBlockingQueue,两者都是先进先出(FIFO)顺序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可选的边界。下面这是一个完整的生产者消费者代码例子,对比传统的wait、nofity代码,它更易于理解。
package 生产者消费者2; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; public class PublicBoxQueue { public static void main(String []args)
{
//定义了一个大小为5的盒子
BlockingQueue publicBoxQueue= new LinkedBlockingQueue(5); Thread pro= new Thread(new Producer(publicBoxQueue));
Thread con= new Thread(new Consumer(publicBoxQueue)); pro.start();
con.start();
} } //生产者
class Producer implements Runnable {
private final BlockingQueue proQueue; public Producer(BlockingQueue proQueue)
{
this .proQueue =proQueue;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i=0;i<10;i++)
{
try {
System. out .println("生产者生产的苹果编号为 : " +(i+1)); //放入十个苹果编号 为1到10
proQueue .put(i+1);
//Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
} } //消费者
class Consumer implements Runnable {
private final BlockingQueue conQueue; public Consumer(BlockingQueue conQueue)
{
this .conQueue =conQueue;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i=0;i<10;i++)
{
try {
System. out .println("消费者消费的苹果编号为 :" +conQueue .take());
//Thread. sleep(3000); //在这里sleep是为了看的更加清楚些 } catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
} }
生产者生产的苹果编号为 : 1
生产者生产的苹果编号为 : 2
生产者生产的苹果编号为 : 3
消费者消费的苹果编号为 :1
消费者消费的苹果编号为 :2
消费者消费的苹果编号为 :3
生产者生产的苹果编号为 : 4
生产者生产的苹果编号为 : 5
消费者消费的苹果编号为 :4
消费者消费的苹果编号为 :5
生产者生产的苹果编号为 : 6
生产者生产的苹果编号为 : 7
生产者生产的苹果编号为 : 8
生产者生产的苹果编号为 : 9
生产者生产的苹果编号为 : 10
消费者消费的苹果编号为 :6
消费者消费的苹果编号为 :7
消费者消费的苹果编号为 :8
消费者消费的苹果编号为 :9
消费者消费的苹果编号为 :10
生产者消费者模式的好处
它的确是一种实用的设计模式,常用于编写多线程或并发代码。下面是它的一些优点:
- 它简化的开发,你可以独立地或并发的编写消费者和生产者,它仅仅只需知道共享对象是谁
- 生产者不需要知道谁是消费者或者有多少消费者,对消费者来说也是一样
- 生产者和消费者可以以不同的速度执行
- 分离的消费者和生产者在功能上能写出更简洁、可读、易维护的代码
java 多线程并发系列之 生产者消费者模式的两种实现的更多相关文章
- Java实现多线程生产者消费者模式的两种方法
生产者消费者模式:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据.生产者生产一个,消费者消费一个,不断循环. 第一种实现方法,用BlockingQueue阻塞队 ...
- Java多线程—阻塞队列和生产者-消费者模式
阻塞队列支持生产者-消费者这种设计模式.该模式将“找出需要完成的工作”与“执行工作”这两个过程分离开来,并把工作项放入一个“待完成“列表中以便在随后处理,而不是找出后立即处理.生产者-消费者模式能简化 ...
- Java多线程-----实现生产者消费者模式的几种方式
1 生产者消费者模式概述 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理 ...
- Java 多线程学习笔记:生产者消费者问题
前言:最近在学习Java多线程,看到ImportNew网上有网友翻译的一篇文章<阻塞队列实现生产者消费者模式>.在文中,使用的是Java的concurrent包中的阻塞队列来实现.在看完后 ...
- java23种设计模式专攻:生产者-消费者模式的三种实现方式
公司的架构用到了dubbo.带我那小哥也是个半吊子,顺便就考我生产者消费者模式,顺便还考我23种java设计模式,
- Java笔记1 : 在生产者消费者模式中,线程通信与共享数据,死锁问题与解决办法
本例定义了4个类,这里说一下,方便下面讲解.分别是Product(产品),Producer(生产者),Consumer(消费者), Test(测试类). 多线程之间通信与共享数据只要引用同一内存区域就 ...
- java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)
-闭锁(Latch) 闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态.通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过, ...
- Java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)
JAVA并发包中有三个类用于同步一批线程的行为,分别是闭锁(Latch),信号灯(Semaphore)和栅栏(CyclicBarrier).本贴主要说明闭锁(Latch)和栅栏(CyclicBarri ...
- 使用Lock锁生产者消费者模式
package com.java.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurren ...
随机推荐
- 【Codeforces 1037D】Valid BFS?
[链接] 我是链接,点我呀:) [题意] 让你判断一个序列是否可能为一个bfs的序列 [题解] 先dfs出来每一层有多少个点,以及每个点是属于哪一层的. 每一层的bfs如果有先后顺序的话,下一层的节点 ...
- python写第一个网页
1>安装django见别的博客http://djangobook.py3k.cn/2.0/chapter03/ 2>运行开发服务器 在命令提示符里进入目录mytest:cd: mytest ...
- 线段树题集 (cf版)
lazy区间修改 : http://acm.hdu.edu.cn/showproblem.php?pid=4902 (hdu4902) http://acm.hdu.edu.cn/showpr ...
- 深刻理解Python中的元类(metaclass)--代码实践
根据http://blog.jobbole.com/21351/所作的代码实践. 这篇讲得不错,但以我现在的水平,用到的机会是很少的啦... #coding=utf-8 class ObjectCre ...
- O - Treats for the Cows 区间DP
FJ has purchased N (1 <= N <= 2000) yummy treats for the cows who get money for giving vast am ...
- 洛谷——P2212 [USACO14MAR]浇地Watering the Fields
P2212 [USACO14MAR]浇地Watering the Fields 题目描述 Due to a lack of rain, Farmer John wants to build an ir ...
- Ubuntu 16.04安装FTP客户端filezilla
1.安装: sudo apt-get install filezilla 参考: http://os.51cto.com/art/201103/247564.htm
- Ubuntu 16.04粘贴板增强工具Parcellite
默认在Ubuntu已经有粘贴板增强工具,比如保存最近五个的复制文本历史. 1.系统默认 使用:[Ctrl]+[;] 然后用数字进行选择即可,但是只支持文本,只能保存最近5次 2.安装Parcellit ...
- java ee标准DataSource理解
- JS执行环境栈及事件循环机制—简洁明了的讲解
JavaScript解释器在浏览器中是单线程的,这意味着浏览器在同一时间内只执行一个事件,对于其他的事件我们把它们排队在一个称为 执行栈(调用栈) 的地方.下表是一个单线程栈的抽象视图: 我们已经知道 ...