基于线程实现的生产者消费者模型(Object.wait(),Object.notify()方法)

需求背景

利用线程来模拟生产者和消费者模型

系统建模

这个系统涉及到三个角色,生产者,消费者,任务队列,三个角色之间的关系非常简单,生产者和消费者拥有一个任务队列的引用,生产者负责往队列中放置对象(id),消费者负责从队列中获取对象(id),其关联关系如下

方案1

因为是多线程操作,所以对任务的存取都要使用线程同步加锁机制,看一下我们的TaskQueue类,两个主方法都加了synchronized修饰,这就意味着,一个时间点只可能有一个线程对这个方法进行操作

TaskQueue类代码

[java] view plain copy

print?

  1. package com.crazycoder2010.thread;
  2. public class TaskQueue {
  3. private int id;
  4. public synchronized void put(int id){
  5. this.id = id;
  6. System.out.println("Put:"+id);
  7. }
  8. public synchronized int get(){
  9. System.out.println("Got:"+this.id);
  10. return this.id;
  11. }
  12. }

再来看一下生产者,这个主要不停的往TaskQueue中添加新对象,这里我们搞了个死循环,运行时不断的对当前数字做加一操作如下

Producer类代码

[java] view plain copy

print?

  1. package com.crazycoder2010.thread;
  2. public class Producer implements Runnable{
  3. private TaskQueue taskQuery;
  4. public Producer(TaskQueue taskQuery){
  5. this.taskQuery = taskQuery;
  6. new Thread(this).start();
  7. }
  8. @Override
  9. public void run() {
  10. int i = 0;
  11. while(true){
  12. taskQuery.put(i++);
  13. }
  14. }
  15. }

消费者对象也是基于线程实现,在循环中不停的轮训TaskQuery.get()来获取当前对象

Consumer类

[java] view plain copy

print?

  1. package com.crazycoder2010.thread;
  2. public class Consumer implements Runnable {
  3. private TaskQueue taskQuery;
  4. public Consumer(TaskQueue taskQuery){
  5. this.taskQuery = taskQuery;
  6. new Thread(this).start();
  7. }
  8. @Override
  9. public void run() {
  10. while(true){
  11. taskQuery.get();
  12. }
  13. }
  14. }

运行一下,看看结果是否是我们所期望的那样

Launcher类代码

[java] view plain copy

print?

  1. package com.crazycoder2010.thread;
  2. public class Launcher {
  3. public static void main(String[] args) {
  4. TaskQueue taskQuery = new TaskQueue();
  5. new Producer(taskQuery);
  6. new Consumer(taskQuery);
  7. System.out.println("Press Control-C to stop.");
  8. }
  9. }

输出结果:

...

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

[java] view plain copy

print?

  1. package com.crazycoder2010.thread;
  2. public class TaskQuery2 {
  3. private int id;
  4. private boolean valueSet;
  5. public synchronized int get(){
  6. if(!valueSet){
  7. try {
  8. wait();
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. valueSet = false;
  14. notify();
  15. System.out.println("Got:"+this.id);
  16. return this.id;
  17. }
  18. public synchronized void put(int id){
  19. if(valueSet){
  20. try {
  21. wait();
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. this.id = id;
  27. valueSet = true;
  28. System.out.println("Put:"+id);
  29. notify();
  30. }
  31. }

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

时间: 2024-10-14 15:00:04

基于线程实现的生产者消费者模型(Object.wait(),Object.notify()方法)的相关文章

11.python并发入门(part8 基于线程队列实现生产者消费者模型)

一.什么是生产者消费者模型? 生产者就是生产数据的线程,消费者指的就是消费数据的线程. 在多线程开发过程中,生产者的速度比消费者的速度快,那么生产者就必须等待消费者把数据处理完,生产者才会产生新的数据,相对的,如果消费者处理数据的速度大于生产者,那么消费者就必须等待生产者. 为了解决这种问题,就有了生产者消费者模型. 生产者与消费者模型,是通过一个容器,来解决生产者和消费者之间的耦合性问题,生产者和消费者之间并不会直接通信,这样生产者就无需等待消费者处理完数据,生产者可以直接把数据扔给队列,这个

多线程学习-基础(十二)生产者消费者模型:wait(),sleep(),notify()实现

一.多线程模型一:生产者消费者模型   (1)模型图:(从网上找的图,清晰明了) (2)生产者消费者模型原理说明: 这个模型核心是围绕着一个"仓库"的概念,生产者消费者都是围绕着:"仓库"来进行操作,一个仓库同时只能被一个生产者线程或一个消费者线程所操作,synchronized锁住的也是这个仓库,仓库是一个容器,所以会有边界值,0和仓库可存放上限,在这个上限内,可以设置多种级别,不同的级别可以执行不同的策略流程. (3)本案例使用知识点: Thread.curre

python2.0_s12_day9之day8遗留知识(queue队列&生产者消费者模型)

4.线程 1.语法 2.join 3.线程锁之Lock\Rlock\信号量 4.将线程变为守护进程 5.Event事件 * 6.queue队列 * 7.生产者消费者模型 4.6 queue队列 queue非常有用,当信息必须安全的在多个线程之间进行数据交换的时候就应该想到queue 所以,queue它能保证数据被安全的在多个线程之间进行交换,那他就是天生的线程安全. queue有那么几种: class queue.Queue(maxsize=0) # 先入先出 class queue.LifoQ

【Windows】用信号量实现生产者-消费者模型

线程并发的生产者-消费者模型: 1.两个进程对同一个内存资源进行操作,一个是生产者,一个是消费者. 2.生产者往共享内存资源填充数据,如果区域满,则等待消费者消费数据. 3.消费者从共享内存资源取数据,如果区域空,则等待生产者填充数据. 4.生产者的填充数据行为和消费者的消费数据行为不可在同一时间发生. 下面用Windows的信号量以及线程等API模拟生产者-消费者模型 #include <Windows.h> #include <stdio.h> #define N 100 #d

基于POSIX的信号量的生产者消费者模型

信号量和Mutex类似,表示可用资源的数量,和Mutex不同的是,这个数量可以大于1,即如果信号量描述的资源数目是1时,此时的信号量和互斥锁相同. 下面我们看看POSIX semaphore库函数,它既可以用于同一进程的线程间同步,也可以用于不同进程间的同步. 1. int sem_init(sem_t *sem,int pshared,unsigned int value) 我们可以用此函数来创建一个未命名的信号量,pshared参数表明是否在多个进程中使用信号量,如果是,将其设置为非0 值,

13 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件  queue队列 生产者消费者模型 Queue队列 开发一个线程池

本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Queue队列 开发一个线程池 进程 语法 进程间通讯 进程池 操作系统发展史 手工操作(无操作系统) 1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式. 手工操作程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把

进击的Python【第九章】:paramiko模块、线程与进程、各种线程锁、queue队列、生产者消费者模型

一.paramiko模块 他是什么东西? paramiko模块是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接. 先来个实例: 1 import paramiko 2 # 创建SSH对象 3 ssh = paramiko.SSHClient() 4 5 # 允许连接不在know_hosts文件中的主机 6 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 7 # 连接服务器 8 ss

Java线程:并发协作-生产者消费者模型

对于多线程程序来说,不管任何编程语言,生产者消费者模型都是最经典的. 实际上,准确的说应该是"生产者-消费者-仓储"模型,离开了仓储,生产者消费者模型就显得没有说服力了. 对于此模型,应该明确以下几点: 生产者仅仅在仓储未满时候生产,仓满则停止生产. 消费者仅仅在仓储有产品时候才能消费,仓空则等待. 当消费者发现仓储没有产品的时候会通知生产者生产. 生产者在生产出可消费产品时候,应该通知消费者去消费. 此模型将要结合java.lang.Object的wait与notify,notify

Python学习笔记——进阶篇【第九周】———线程、进程、协程篇(队列Queue和生产者消费者模型)

Python之路,进程.线程.协程篇 本节内容 进程.与线程区别 cpu运行原理 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Queue队列 开发一个线程池 进程 语法 进程间通讯 进程池 参考链接http://www.cnblogs.com/alex3714/articles/5230609.html