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 ...
随机推荐
- Discuz论坛迁移需要修改的3个配置文件
需要修改的3个地方: \config\config_global.php \config\config_ucenter.php \uc_server\data\config.inc.php
- gdb个人使用记录
参考博客:https://blog.csdn.net/zdy0_2004/article/details/80102076 安装gdb,查看版本确认成功: sudo apt install gdb g ...
- [luoguP1586] 四方定理(DP 背包)
传送门 相当于背包, f[i][j] 表示当前数为 i,能分解成 j 个数的平方的和的数量 那么就是统计背包装物品的数量 ——代码 #include <cmath> #include &l ...
- [luoguP1280] 尼克的任务(DP)
传送门 原本想着 f[i] 表示前 i 个任务的最优答案,但是不好转移 看了题解后,发现是 f[i] 表示前 i 分钟的最优解,看来还是不能死脑筋,思维得活跃,一个思路行不通就换一个思路. 把 f 数 ...
- vue.js编程式路由导航 --- 由浅入深
编程式路由导航 实例中定义一个方法,这个方法绑定在标签上 然后就设置路由跳转 语法 this.$router.history.push('要跳转路由的地址') <!DOCTYPE html> ...
- jsp之${CTX}理解
jsp之${CTX} 根据自己的需要选择以下标签. <%@ taglib uri="/struts-tags" prefix="s"%> <% ...
- nyoj_600_花儿朵朵_201404162000
花儿朵朵 时间限制:1000 ms | 内存限制:65535 KB 难度:5 描述 春天到了,花儿朵朵盛开,hrdv是一座大花园的主人,在他的花园里种着许多种鲜花,每当这个时候,就会有一大群游 ...
- Contemplation! Algebra 矩阵快速幂
Given the value of a+b and ab you will have to find the value of a n + b n Input The input file cont ...
- 今天又学到了一个很重要的公式,(a+b)^n,组合数的求和,牛逼,为自己鼓掌👏
另外还有: C(n, 0) + ... + C(n, n) = 2^n 其实从上面的二项式定理,也可以推导出来的.
- ubuntu 搭建 svn服务器,使用http方式访问
原文: http://blog.csdn.net/wobuxingfang/article/details/70835414 参考:http://www.cnblogs.com/zzugyl/p/36 ...