基于线程实现的生产者消费者模型(Object.wait(),Object.notify()方法)
需求背景
利用线程来模拟生产者和消费者模型
系统建模
这个系统涉及到三个角色,生产者,消费者,任务队列,三个角色之间的关系非常简单,生产者和消费者拥有一个任务队列的引用,生产者负责往队列中放置对象(id),消费者负责从队列中获取对象(id),其关联关系如下

方案1
因为是多线程操作,所以对任务的存取都要使用线程同步加锁机制,看一下我们的TaskQueue类,两个主方法都加了synchronized修饰,这就意味着,一个时间点只可能有一个线程对这个方法进行操作
TaskQueue类代码
- package com.crazycoder2010.thread;
- public class TaskQueue {
- private int id;
- public synchronized void put(int id){
- this.id = id;
- System.out.println("Put:"+id);
- }
- public synchronized int get(){
- System.out.println("Got:"+this.id);
- return this.id;
- }
- }
再来看一下生产者,这个主要不停的往TaskQueue中添加新对象,这里我们搞了个死循环,运行时不断的对当前数字做加一操作如下
Producer类代码
- package com.crazycoder2010.thread;
- public class Producer implements Runnable{
- private TaskQueue taskQuery;
- public Producer(TaskQueue taskQuery){
- this.taskQuery = taskQuery;
- new Thread(this).start();
- }
- @Override
- public void run() {
- int i = 0;
- while(true){
- taskQuery.put(i++);
- }
- }
- }
消费者对象也是基于线程实现,在循环中不停的轮训TaskQuery.get()来获取当前对象
Consumer类
- package com.crazycoder2010.thread;
- public class Consumer implements Runnable {
- private TaskQueue taskQuery;
- public Consumer(TaskQueue taskQuery){
- this.taskQuery = taskQuery;
- new Thread(this).start();
- }
- @Override
- public void run() {
- while(true){
- taskQuery.get();
- }
- }
- }
运行一下,看看结果是否是我们所期望的那样
Launcher类代码
- package com.crazycoder2010.thread;
- public class Launcher {
- public static void main(String[] args) {
- TaskQueue taskQuery = new TaskQueue();
- new Producer(taskQuery);
- new Consumer(taskQuery);
- System.out.println("Press Control-C to stop.");
- }
- }
输出结果:
...
Put:58
Put:59
Put:60
Put:61
Put:62
Put:63
Put:64
Got:64
Got:64
Got:64
Put:65
Put:66
...
问题出现在哪里呢?
从结果输出我们可以看出,生产者的对象放入顺序是按次序进行的,但是消费者读取对象的数序很奇怪,一段时间内读取同一个数值,这个是怎么造成的呢?
我们知道,当启动线程后线程什么时候被jvm所调度是不确定的,上面的结果在不同的机器上运行很可能得到不同的结果,当Producer线程被调度运行一段时间后,线程Consumer获取到运行资格,然后从TaskQueue中取对象,这个时候由于Producer处于等待被调度状态,所以TaskQueue中的id一直都是个固定值,所以这个时候Consumer获取到的一直都是Producer在被jvm设置为等待状态那一刻的值,运行一段时间,Producer又被jvm调度器调度,获取运行资格,这个时候id继续从上次暂定时的值开始累加,依次循环,然后就得到了上面的运行结果
方案2
这个方案里我们要对方案1做一些改造,当有Producer生产出一个id时,直到有ConSumer来将他拿走,然后再生产下一个id,Consumer也是类似,直到Producer生产出id后才来取,否则一直等待下去
解决:
Object类里有个wait()方法和notify()/notifyAll(),一直很神秘,先前没怎么用过,看了一下原来这个东东是与线程同步操作密接相关的,
比如我们在应用中如果要对某一个方法或某个代码段做线程同步控制,那么需要为这个方法添加synchronized修饰或者是synchronized(obj){},这个obj可以理解成我们实际生活中房间的一把锁,默认情况下,当一个线程拥有了一个方法或代码段的锁后,就可以进入房间(方法或代码块)任意干坏事,而其他的线程只能在门外等待直到当前线程执行完毕打开房间(释放锁),而Object的wait和notify则是给这个锁提供了一些更先进的功能,这个锁可以自己开锁wait()(让同步方法或代码块暂时放弃占用的锁),进而让别的线程有机会进入运行,而当实际成熟时(满足运行的条件)又可以把自身锁住notify(),进而又继续进入上次被暂停的操作
改造后的TaskQueue2
- package com.crazycoder2010.thread;
- public class TaskQuery2 {
- private int id;
- private boolean valueSet;
- public synchronized int get(){
- if(!valueSet){
- try {
- wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- valueSet = false;
- notify();
- System.out.println("Got:"+this.id);
- return this.id;
- }
- public synchronized void put(int id){
- if(valueSet){
- try {
- wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- this.id = id;
- valueSet = true;
- System.out.println("Put:"+id);
- notify();
- }
- }
Producer,Consumer和Launcher类的代码不改动
运行结果如下
Put:0
Got:0
Put:1
Got:1
Put:2
Got:2
Put:3
Got:3
Put:4
Got:4
Put:5
Got:5
Put:6
Got:6
基于线程实现的生产者消费者模型(Object.wait(),Object.notify()方法)的更多相关文章
- 线程锁、threading.local(flask源码中用的到)、线程池、生产者消费者模型
一.线程锁 线程安全,多线程操作时,内部会让所有线程排队处理.如:list/dict/Queue 线程不安全 + 人(锁) => 排队处理 1.RLock/Lock:一次放一个 a.创建10个线 ...
- 锁丶threading.local丶线程池丶生产者消费者模型
一丶锁 线程安全: 线程安全能够保证多个线程同时执行时程序依旧运行正确, 而且要保证对于共享的数据,可以由多个线程存取,但是同一时刻只能有一个线程进行存取. import threading v = ...
- java多线程:线程间通信——生产者消费者模型
一.背景 && 定义 多线程环境下,只要有并发问题,就要保证数据的安全性,一般指的是通过 synchronized 来进行同步. 另一个问题是,多个线程之间如何协作呢? 我们看一个仓库 ...
- 多线程学习-基础(十二)生产者消费者模型:wait(),sleep(),notify()实现
一.多线程模型一:生产者消费者模型 (1)模型图:(从网上找的图,清晰明了) (2)生产者消费者模型原理说明: 这个模型核心是围绕着一个“仓库”的概念,生产者消费者都是围绕着:“仓库”来进行操作, ...
- python网络编程--进程(方法和通信),锁, 队列,生产者消费者模型
1.进程 正在进行的一个过程或者说一个任务.负责执行任务的是cpu 进程(Process: 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在 ...
- 【Windows】用信号量实现生产者-消费者模型
线程并发的生产者-消费者模型: 1.两个进程对同一个内存资源进行操作,一个是生产者,一个是消费者. 2.生产者往共享内存资源填充数据,如果区域满,则等待消费者消费数据. 3.消费者从共享内存资源取数据 ...
- python2.0_s12_day9之day8遗留知识(queue队列&生产者消费者模型)
4.线程 1.语法 2.join 3.线程锁之Lock\Rlock\信号量 4.将线程变为守护进程 5.Event事件 * 6.queue队列 * 7.生产者消费者模型 4.6 queue队列 que ...
- 第23章 java线程通信——生产者/消费者模型案例
第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...
- 进程,线程,GIL,Python多线程,生产者消费者模型都是什么鬼
1. 操作系统基本知识,进程,线程 CPU是计算机的核心,承担了所有的计算任务: 操作系统是计算机的管理者,它负责任务的调度.资源的分配和管理,统领整个计算机硬件:那么操作系统是如何进行任务调度的呢? ...
随机推荐
- Using Integrated SOA Gateway in Oracle EBS(websevice)
http://blog.csdn.net/pan_tian/article/details/10159935 Oracle EBS如何与第三方系统相集成?比如这样的需求,X系统知道物料编码,需要从EB ...
- Delphi Dll 动态调用例子(1)
http://blog.sina.com.cn/s/blog_62c46c3701010q7h.html 一.编写dll library TestDllByD2007; uses SysUtils, ...
- EGOCache缓存框架详细讲解
EGOCache是一个轻量级的缓存框架.用法简单方便,在现在的项目中,我就用到EGOCache来缓存下载过的照片和字符串. 有人可能会问到,缓存照片还需要用EGOCache吗?AFNetworking ...
- PhoneGap原理
http://www.oschina.net/question/213217_46380
- JIT与JVM的三种执行模式:解释模式、编译模式、混合模式
Java JIT(just in time)即时编译器是sun公司采用了hotspot虚拟机取代其开发的classic vm之后引入的一项技术,目的在于提高java程序的性能,改变人们“java比C/ ...
- Python 将字典的元素按照键或者值的大小进行排序
在开发的过程中有时遇到这样的需求,一个字典里保存了一份完整的数据,其中键是一个id,值是时间,需要获取最新的5条数据,处理方式如下: 假设字典数据的变量名为my_dict data_list = so ...
- git 拖下laravel 代码后报错 Warning: require(D:\WWW\laravel\bootstrap/../vendor/autoload.php
omposer install 执行 Problem 1 - Installation request for doctrine/annotations v1.5.0 -> sat ...
- FunDA(15)- 示范:任务并行运算 - user task parallel execution
FunDA的并行运算施用就是对用户自定义函数的并行运算.原理上就是把一个输入流截分成多个输入流并行地输入到一个自定义函数的多个运行实例.这些函数运行实例同时在各自不同的线程里同步运算直至耗尽所有输入. ...
- 使用python进行短信轰炸
本文作者:i春秋作家——Hacker1ee 大家好,我是1ee(因为在作家群,就不加Hacker这个前缀了,怕被大佬打..) 刚加入i春秋作家组希望大家多多关照,也欢迎大家找我交流 今天我来讲讲我最近 ...
- 【xsy1378】 水题7号 贪心
题目大意:有$m$组约束关系$(x_i,y_i)$,你要构造一个排列,满足数$x_i$出现在数$y_i$前面,请使得这个排列字典序最小,请输出这个排列.无解请输出-1. 数据范围:$n,m≤10^5$ ...