zoukankan      html  css  js  c++  java
  • 生产者和消费者模型

    生产者和消费者模型

    线程通信:不同的线程执行不同的任务,如果这些任务有某种关系,各个线程必须要能够通信,从而完成工作。线程通信中的经典问题:生产者和消费者问题

    模型:

    这个模型也体现了面向对象的设计理念:低耦合

    也就是为什么生产者生产的东西为什么不直接给消费者,还有经过一个缓冲区(共享资源区)

    这就相当于去包子店吃包子,你要5个包子,老板把5个人包子放在一个盘子中再给你,这个盘子就是一个缓冲区。

    现在模拟一个生产者和消费者模型

    • 生产者生产“李小龙 男”,消费者消费也就是打印出“李小龙 男”

    • 生产者生产“狗晗 女”,消费者消费也就是打印出“狗晗 女”

    • 要求生产者生产一个,消费者消费一个。也就是缓冲区为1

    • 期望结果:

      李小龙 男

      狗晗 女

      李小龙 男

      狗晗 女

      ···

    解决问题

    需要创建4个类,分别是生产者,消费者,共享资源类,测试类

    • 生产者类实现Runnable接口,覆盖run方法

      public class Producer implements Runnable{
      	ShareResource resource = null;
      	public Producer(ShareResource resource){
      		this.resource = resource;
      	}
      	@Override
      	public void run() {
      		for(int i = 0; i < 50;i++){
      			if(i % 2 == 0){
      				resource.product("李小龙", "男");
      			}else{
      				resource.product("狗晗","女");
      			}
      		}
      	}
      	
      }
      
    • 消费者类实现Runnable接口,覆盖run方法

      public class Consumer implements Runnable {
      	ShareResource resource = null;
      	public Consumer(ShareResource resource){
      		this.resource = resource;
      	}
      	public void run(){
      		for(int i = 0;i < 50;i++){
      			resource.consume();
      		}
      	}
      }
      
    • 共享资源类有生产者和消费者两个类的引用

      public class ShareResource {
      	private String name;
      	private String sex;
      	
      	public void product(String name,String sex){
      	    this.name = name;
              try{
                  Thread.sleep(10);
              }catch(Exception e){
                  e.printStackTrace();
              }
      		this.sex = sex;
      	}
      	
      	public void consume(){
              try{
                  Thread.sleep(10);
              }catch(Exception e){
                  e.printStackTrace();
              }
      		System.out.println(this.name + " " + this.sex);
      	}
      }
      
    • 测试类可以启动线程

      public class Demo {
      	public static void main(String[]args){
      		ShareResource resource = new ShareResource();
      		new Thread(new Producer(resource)).start();
      		new Thread(new Consumer(resource)).start();
      	}
      }
      

    运行后发现,结果性别发生了紊乱:

    我靠,这我龙哥愿意吗,怎么回事?

    就是因为多线程并发同一资源,要把它们的方法用synchronized修饰保证它们生产和消费不同时进行,当你加上synchronized之后:

    确实性别紊乱的问题是解决了,狗晗是高兴了,但是它们并不是交替出现的啊,怎么回事?

    你要交替出现,必须一个等着一个啊,你生产完了,你得停下来休息啊,让消费者先消费,等到消费者消费完之后,让消费者把你叫醒再继续生产,消费者就又去休息去了,这个消费者优点类似于吃了睡,睡了在吃一样。

    wait和notify方法

    • wait方法:执行该方法的对象释放同步锁,JVM把该线程存放到等待池中,等待其他线程唤醒

    • notify方法:唤醒在等待池中的等待的任意一个线程,把线程转移到锁池

    • notifyAll方法:唤醒在等待池中等待的所有线程,把线程转移到锁池

      等待池:没有机会获取同步锁,只能等待被唤醒,被转移到锁池中。

      锁池: 当前生产者在生产数据的时候(先拥有同步锁),其他线程就在锁池中等待获取锁.,当线程执行完同步代码块的时候,就会释放同步锁,其他线程开始抢锁的使用权.

    又来看这个模型,应该得定义一个判断当前共享资源区是否为空,如果为空,消费者就应该等待生产者生产,当生产者生产 完毕后应该唤醒消费者进行消费。如果不为空,生产者就应该等待消费者,等消费者完毕后再唤醒生产者继续生产

    public class ShareResource {
    	private String name;
    	private String sex;
    	private boolean isEmpty = true;//判断共享区空和满的一个标志
    	//生产者生产
    	synchronized public void product(String name,String sex){
    		try {
    			while(!isEmpty){//共享区不空,生产者需要停下等待消费者消费
    				/**
    				 * this是同步锁(同步监听对象),wait方法是Object类中 的方法,调用该方法就释放同步锁
    				 * 然后JVM把该线程存放到等待池中,等待其他线程唤醒该线程
    				 * 该方法只能被同步监听对象调用,否则报错IllegalMonitorStateException.
    				 */
    				
    				this.wait();
    			}
    			//----------开始生产-----------
    			this.name = name;
    			Thread.sleep(10);
    			this.sex = sex;
    			//---------结束生产------------
    			isEmpty = false;  //设置共享区为不空
    			/**
    			 * notify方法是唤醒等待池中的随机一个线程,把线程转移到锁池中等待(锁池中有机会获取到锁)
    			 * notifyAll方法是唤醒等待池中的所有线程,把线程转移到锁池中等待
    			 * 该方法只能被同步监听对象锁调用,否则报错
    			 */
    			this.notify();	//唤醒一个消费者               notify是唤醒其中一个,notifyAll是唤醒全部
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	//消费者消费
    	synchronized public void consume(){
    		try {
    			while(isEmpty){//当共享区为空时,消费者进行等待
    				this.wait();
    			}
    			//------------开始消费-------------
    			Thread.sleep(30);
    			System.out.println(name + " " + sex);
    			//------------结束消费-------------
    			isEmpty = true;
    			this.notify();
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	} 
    }
    

    用synchronized修饰方法,会自动获取锁,自动释放锁。从java5开始使用Lock机制取代synchronized代码块和synchronized方法,使用Condition接口对象的await,signal,signalAll方法取代notify,notifyAll,用法还是大同小异

    public class ShareResource {
    	private String name;
    	private String sex;
    	private boolean isEmpty = true;
    	private final Lock lock = new ReentrantLock();
    	private Condition condition = lock.newCondition();
    	
    	public void product(String name,String sex){
    		lock.lock();//获取锁
    		try {
    			while(!isEmpty){//不空,生产者要等待消费者消费
    				condition.await();//等待,相当于this.wait();
    			}
    			this.name = name;
    			Thread.sleep(10);
    			this.sex = sex;
    			
    			isEmpty = false;//把共享区置为不空
    			condition.signal();//唤醒一个线程,相当于this.notify().  signalAll 可以唤醒全部线程
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally{
    			lock.unlock();//释放锁
    		}
    	}
    	
    	public void consume(){
    		lock.lock();
    		try {
    			while(isEmpty){//共享区为空,需要等待生产者生产
    				condition.await();
    			}
    			
    			Thread.sleep(20);
    			System.out.println(this.name + " " + this.sex);
    			
    			isEmpty = false;
    			condition.signal();
    			
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally{
    			lock.unlock();
    		}
    	}
    }
    
    

    结果就是:

    Lock机制

    创建一个Lock接口实现类ReetranLock的对象,进入同步方法后立即加锁,lock.lock();最后释放锁,看API中的典型代码

  • 相关阅读:
    linux ——process
    linux ipc—msgqueue
    JDEclipse源码查看
    用 AXIOM 促进 XML 处理
    使jets3t支持https
    正版太贵了……
    Axis2学习笔记 (Axiom 篇 )
    基于Hadoop的海量文本处理系统介绍
    详解ASP.NET MVC的请求生命周期
    .Net相关的资源连接
  • 原文地址:https://www.cnblogs.com/tfper/p/9862587.html
Copyright © 2011-2022 走看看