2.5多线程(Java学习笔记)生产者消费者模式
一、什么是生产者消费者模式
生产者生产数据存放在缓冲区,消费者从缓冲区拿出数据处理。

可能大家会问这样有何好处?
1.解耦
由于有了缓冲区,生产者和消费者之间不直接依赖,耦合度降低,便于程序拓展和维护。
如果没有缓冲区消费者与生产者是直连的,改动生产者可能对消费者造成影响。
2.并发处理,提升效率
消费者和生产者分离后,两者不形成依赖可以独立运行,提高了效率。
如果是消费者和生产者是直接接触没有缓冲区,假如消费者消费太慢,生产者也只能等待消费者消费完。这样浪费资源。
中间多个缓冲区后,消费者消费慢生产者可以先把生产的东西放在缓冲区,然后等待消费者慢慢消费。
举个例子,就像寄信我们是寄信人可以看做生产者(生产了信)。邮递员看做消费者(处理我们的信),缓冲区就看做是邮筒。
没有缓冲区的话我们就要直接和快递员打交道,假如我们做出什么更改邮递员也需要同样做出更改满足我们的需求。
而且没有邮筒的话,而且每次邮递员都需要找我们取信,效率低下。
假如邮递员处理较慢我们只能等着极其不方便,如果有了邮筒邮递员比较忙我们可以先放在邮筒里等邮递员慢慢处理,这样不妨碍我们寄信。
接下来我们梳理下生产者消费者模式的基本思路。

要实现消费者生产者模式我们还需要知道几个函数
1.wait(),让当前线程等待且会释放对象锁,这时wait()之后的语句不会执行。只有被其他线程用notify或notifyall唤醒,才能重新抢夺锁,获得锁后从wait()方法之后的路径继续执行。
如果没有被其他线程唤醒,就不会醒来。
2.nitify()通知在此对象监视器上等待的单个线程醒来,只是通知而且通知是随机的。当前对象监视器上等待的线程获得通知后会金进入等待队列抢夺对象锁。
3.notifyall唤醒在此对象监视器上等待的所有线程。
注意上面的函数都需要获得对象锁,所以需要synchronized配合使用。
接下来就是具体的代码了:
缓冲区:
public class Buffer {
private int index = 0;
private int MAX = 5; //定义缓冲区的最大存储量
private Phone pa[] = new Phone[MAX];//定义存储产品的数组
synchronized public void consumer(){ //消费者方法,要加synchronized获得锁,消费线程会进缓冲区拿东西。
if(index <= 0){ //如果缓冲区没有资源,当前线程等待并释放锁。
try {
wait(); //如果该线程被其他线程唤醒并获得锁,从wait()之后继续执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 消费时一定要注意先--,因为必定先生产后才能消费,
* 而生产后由于++的原因会多加一个1,所以这里需要多减一个1.
* 仓库有货或生产者线程唤醒后消费产品。
* 生产者是先生产然后唤醒消费者,所以唤醒消费者后的缓冲区必有货物.
*/
//从缓冲区拿手机,即消费手机。
System.out.println("消费手机:" + pa[--index].getPhoneNum());
notify(); //消费后唤醒生产者线程。
/**
* 这里的唤醒只是通知下可以抢夺锁了,但也不一定抢到。
* 如果生产者没有抢到锁消费者会继续消费。
* 如果没有货了,消费者自己会等待,然后生产者自然会去生产。
* 锁只有执行完synchronized范围才会释放锁。wait()是可以直接立刻马上释放锁,
* 而且之后的语句也不执行,之后的语句等到下一次被其他线程唤醒并获得锁后才执行。
*/
}
synchronized public void producer(Phone p){//生产者,接受来自生产线程生产的产品。
if(index >= MAX){ //如果缓冲区已满,则释放锁并等待。
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//将生产的产品放在数组中,放入后要加1
pa[index++] = p; //仓库没有满或被消费者线程唤醒后才能生产。
//被消费者叫醒说明 消费者消费了产品,则缓冲区必定没有满.
System.out.println("生产手机:" + p.getPhoneNum());
notify(); //生产后,唤醒消费者线程。
}
}
产品:
public class Phone {
private int num;
public Phone(int num){ //构造器设置手机编号
this.num = num;
}
public int getPhoneNum(){ //产品中有一个取得当前手机编号的方法。
return num;
}
}
生产者
public class Producer implements Runnable{ //生产者线程需要实现Runnable接口
private int i = 0;
private Buffer b; //创建缓冲区对象。
public void setBuffer(Buffer b){ //添加构造方法获得缓冲区对象。
this.b = b;
}
public void run(){ //生产者线程生产20个产品
for(i = 0; i < 20; i++){
b.producer(new Phone(i)); //生产者线程将产品放入缓冲区
}
}
}
消费者
public class Consumer implements Runnable{
private Buffer b;
public void setBuffer(Buffer b){ //通过方法设置缓冲区对象
this.b = b;
}
public void run(){ //消费者线程
for(int i = 0; i < 20; i++){ //消费20个产品
b.consumer(); //进入缓冲区拿产品
}
}
}
客户端
public class ProCon {
public static void main(String[] args) {
Buffer b = new Buffer(); //建立缓冲区
Consumer c = new Consumer(); //建立消费者线程
c.setBuffer(b); //在消费者线程中放入缓冲区对象
Producer p = new Producer(); //建立生产者线程
p.setBuffer(b); //在生产者线程中放入缓冲区对象
new Thread(c).start();
new Thread(p).start();
}
}
运行结果:
生产手机:0
生产手机:1
生产手机:2
生产手机:3
生产手机:4
消费手机:4
消费手机:3
消费手机:2
消费手机:1
消费手机:0
生产手机:5
生产手机:6
生产手机:7
生产手机:8
生产手机:9
消费手机:9
消费手机:8
消费手机:7
消费手机:6
消费手机:5
生产手机:10
生产手机:11
生产手机:12
生产手机:13
生产手机:14
消费手机:14
消费手机:13
消费手机:12
消费手机:11
消费手机:10
生产手机:15
生产手机:16
生产手机:17
生产手机:18
生产手机:19
消费手机:19
消费手机:18
消费手机:17
消费手机:16
消费手机:15
由于这里缓冲区的容量为5,所以每生产5个缓冲区就满了,然后会通知生产者会等待,消费者回来消费。
当消费者消费了5个后没有货物了,消费者会等待,这时生产者又会来生产。
这只是恰好一个线程运行的时间可以生产完5个或者消费5个,在实际中的顺序是不确定的。
但有一点可以确定,必定是生产好了后的产品才能消费。
而且必定是缓冲区满了生产者会停下,缓冲区没有货物了消费者会停下。
2.5多线程(Java学习笔记)生产者消费者模式的更多相关文章
- Celery 框架学习笔记(生产者消费者模式)
生产者消费者模式 在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产 ...
- Java设计模式之生产者消费者模式
Java设计模式之生产者消费者模式 博客分类: 设计模式 设计模式Java多线程编程thread 转载 对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一 ...
- java 多线程并发系列之 生产者消费者模式的两种实现
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题.该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度. 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据 ...
- java多线程系列15 设计模式 生产者 - 消费者模式
生产者-消费者 生产者消费者模式是一个非常经典的多线程模式,比如我们用到的Mq就是其中一种具体实现 在该模式中 通常会有2类线程,消费者线程和生产者线程 生产者提交用户请求 消费者负责处理生产者提交的 ...
- Java多线程—阻塞队列和生产者-消费者模式
阻塞队列支持生产者-消费者这种设计模式.该模式将“找出需要完成的工作”与“执行工作”这两个过程分离开来,并把工作项放入一个“待完成“列表中以便在随后处理,而不是找出后立即处理.生产者-消费者模式能简化 ...
- Java 并发编程 生产者消费者模式
本文部分摘自<Java 并发编程的艺术> 模式概述 在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的数据.生产者和消费者彼此之间不直接通信,而是通过阻塞队列进行通信,所以生产 ...
- java设计模式之生产者/消费者模式
什么是生产者/消费者模式? 某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产者:而处理数据的模块,就称为消费者 ...
- Java多线程学习笔记--生产消费者模式
实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...
- 多线程学习之三生产者消费者模式Guarded Suspension
Guarded Suspension[生产消费者模式] 一:guarded suspension的参与者--->guardedObject(被防卫)参与者 1.1该 ...
- 2.1多线程(java学习笔记) java中多线程的实现(附静态代理模式)
一.多线程 首先我们要清楚程序.进程.线程的关系. 首先进程从属于程序,线程从属于进程. 程序指计算机执行操作或任务的指令集合,是一个静态的概念. 但我们实际运行程序时,并发程序因为相互制约,具有“执 ...
随机推荐
- 孤荷凌寒自学python第六十二天学习mongoDB的基本操作并进行简单封装1
孤荷凌寒自学python第六十二天学习mongoDB的基本操作并进行简单封装1 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第八天. 今天开始学习mongoDB的简单操作, ...
- Windows添加自定义服务、批处理文件开机自启动方法
[Windows 添加自定义服务方法]: 1.使用Windows服务工具instsrv.exe与srvany.exe: 参考:https://wenku.baidu.com/view/44a6e6f8 ...
- 使用UltraEdit搭建自己的C/C++ IDE
使用UltraEdit搭建自己的C/C++ IDE CodeBlocks的13.12版本啊,主要缺点是启动慢,而且在Windows上容易假死,写着写着就无响应了,然后死活活不过来.所以没办法,只好干脆 ...
- LDA学习笔记
线性判别分析(Linear Discriminant Analysis,简称LDA)是一种经典的线性学习方法.其思想非常朴素,设法将样例投影到一条直线上,使得同类样例的投影点尽可能接近,异类的样例的投 ...
- 网络编程--广播&组播
广播 1.广播地址 如果用{netid, subnetid, hostid}( {网络ID,子网ID,主机ID})表示IPv4地址.那么有四种类型的广播地址,我们用-1表示所有比特位均为1的字段: 1 ...
- NYOJ36 水池数目
水池数目 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 南阳理工学院校园里有一些小河和一些湖泊,现在,我们把它们通一看成水池,假设有一张我们学校的某处的地图,这个地 ...
- MVC从Controller到view进行传值的方法
这几天基本上都是交接的一些杂事,没有什么工作任务,就有空来回顾一下MVC.虽然工作中也用到了MVC,但已经被微软的架构师设计的找不到MVC的影子了,可能有别的考虑吧,至今还没研究出来.所以,今天就来回 ...
- 使用C#创建windows服务程序
创建windows服务项目 一.创建服务 1.文件->新建->项目->windows桌面->windows服务,修改你要的项目名称.我这不改名,仍叫WindowsService ...
- 电阻 (resistance)
电阻 (resistance) 题目描述 每次小x物理作业没做完时,总是会去和老师交流感情,他们之间由此建立起来良好的师生关系.于是有一天,老师带着一道物理难题来见小x. 这道题给出了一个有n个电阻的 ...
- Lesson9 some interesting things in C#
1.关键帧动画 1)xml 界面 <Page x:Class="Test.MainPage" xmlns="http://schemas.microsoft.com ...