`

Java线程协作与消费者生产者模式

 
阅读更多

 

    notify()与wait()

 

     先来说一下notify()、notifyAll()与wait()方法.

    可能会令初学者可能比较困惑的是,作为线程通讯的方式,notify()、notifyAll()与wait()却被定义在Object类上。其实很好理解,wait()的本质是释放已获取的锁对象并阻塞等待被唤醒,而notify()则是释放已获取的锁对象并唤醒在wait()中等待同一个锁的线程。因此调用方式便是

                 [锁对象].wait()

                 或者

                [锁对象].notify().

     若不指明调用对象,实际上就用this关键字代替了[锁对象],即当前对象本身作为锁对象。

     若调用上述代码时并未获取任何锁,或者指定的锁对象和获取的锁不符,都会抛出  java.lang.IllegalMonitorStateException       生产者者与消费者 

       生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间内共用同一个存储空间,如下图所示,生产者向空间里存放数据,而消费者取用数据。

             

   

      需要如下三部分组成:

     1.存储对象:即图中的Shared Buffer。存储对象中防止生产者的产品,同时让消费者消费。

     2.生产者,负责生产商品并添加至存储对象。当存储放满时停止生产。当生产完一个产品时,通知消费者消费。 注意,这里容易产生一个误解:通知消费者消费,并不代表消费者一定会去消费。有可能还未进入消费的代码块时,CPU又可能由于分时的工作原理,放下消费者转而继续执行生产者。又或者可能有多个生产者同时生产。

     3.消费者,负责消费商品。当存储对象中商品为空时等待。

 

实现方式,根据线程通讯操作所处位置,可分为如下两种:

 

  (一) 在消费者和生产者中完成线程通讯

public class Repository {  //仓储类
	private LinkedList<Object> repository = new LinkedList<Object>(); //仓储实现
    private int MAX = 10;  //最大商品数量
    private int count=0;  //当前商品数量
    public boolean add(Object product){
    	count++;
    	return this.repository.add(product);
    }   
    public Object remove(){
    	count--;
    	return this.repository.removeLast();
    }
	public int getMAX() {
		return MAX;
	}
	public int getCount() {
		return count;
	}  
}

 

    

public class Producer  implements Runnable{

	private Repository repository;
	
	public Repository getRepository() {
		return repository;
	}

	public void setRepository(Repository repository) {
		this.repository = repository;
	}

	 public Producer(Repository repository){
		   this.repository=repository;
	   }
	@Override
	public void run() {
		 while (true) {
             synchronized (repository) {
                 try {
                     while (repository.getCount()==repository.getMAX()) {  
                     //当商品数量已满时,暂停生产
                         System.out.println("respositry is full , please wait");
                         repository.wait();
                     }
                     Object newOb = new Object();   
                     if (repository.add(newOb)) {  //生成商品,并唤醒所有该锁的等待者
                         System.out.println("Producer put a Object to respositry");
                         Thread.sleep((long) (Math.random() * 3000));
                         repository.notifyAll();
                     }
                 } catch (InterruptedException ie) {
                     System.out.println("producer is interrupted!");
                 }

             }
         }
		
	}

 

public class Consumer  implements Runnable{

	public Repository respository;
	
	
	
	public Repository getRespository() {
		return respository;
	}



	public void setRespository(Repository respository) {
		this.respository = respository;
	}


   public Consumer(Repository repository){
	   this.respository=repository;
   }
	@Override
	public void run() {
		while (true) {
                synchronized (respository) {
                try {
                    while (respository.getCount() == 0) {
               //当商品为空时,暂停消费                        
                     System.out.println("repositry is empty , please wait");
                      respository.wait();
                    }
                    respository.remove(); //消费商品
                    System.out.println("Comsumer get  a Object from repositry");
                    Thread.sleep((long) (Math.random() * 3000));
                    respository.notifyAll();
                } catch (InterruptedException ie) {
                    System.out.println("Consumer is interrupted");
                }

            }
        }
	}

}

   

public class Client {

    public static void main(String[] args) {
    	 Repository repository=new Repository();
    	 new Thread(new Producer(repository)).start();
         new Thread(new Consumer(repository)).start();
    }
}

 

       我们来看下运行结果,需要明确的是,当生产者完成一个商品的生产时,即时notify唤醒了消费者,但CPU分时可能此时又将运行时间分配给了下一次生产者。所以可能会出现连续生产或者连续消费的情形。

Producer put a Object to respositry
Producer put a Object to respositry
Comsumer get  a Object from repositry
Comsumer get  a Object from repositry
repositry is empty , please wait
Producer put a Object to respositry
Producer put a Object to respositry
Producer put a Object to respositry
Producer put a Object to respositry
Producer put a Object to respositry
Producer put a Object to respositry
Producer put a Object to respositry
Producer put a Object to respositry
Producer put a Object to respositry
Producer put a Object to respositry
respositry is full , please wait
Comsumer get  a Object from repositry
Comsumer get  a Object from repositry

 

(二) 在仓储中封装线程通讯操作

         C把线程通讯代码放置于消费者和生产者的逻辑中,是否让人觉得有些不够清晰? 没问题,可以在仓储中封装包含线程通讯的新增和移除操作。这样生产者和消费者的代码逻辑中,便不再需要线程通讯和同步的相关代码。

      为避免冗余代码太多,下面仅贴出仓储及消费者的代码。客户端代码不变,而生产者代码的变化类似于消费者。

public class Repository {
	private LinkedList<Object> repository = new LinkedList<Object>(); 
	private int MAX = 10; 
	private int count = 0;

	public synchronized void add(Object product) {
		try {
			while (this.count == MAX) {
				System.out.println("full wait");
				wait();
			}
			count++;
			this.repository.add(product);
			System.out.println("producer add a new product");
            Thread.sleep((long) (Math.random() * 3000));
			notifyAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public synchronized void remove() {
		
		try {
			while (this.count == 0) {
				System.out.println("empty wait");
				wait();
			}
			count--;
			System.out.println("consumer consume a  product");
			this.repository.removeLast();
            Thread.sleep((long) (Math.random() * 3000));
			notifyAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public int getMAX() {
		return MAX;
	}

	public int getCount() {
		return count;
	}

}

  

public class Consumer implements Runnable {

	public Repository repository;

	public Repository getRespository() {
		return repository;
	}


	public Consumer(Repository repository) {
		this.repository = repository;
	}

	@Override
	public void run() {
		while (true) {
				repository.remove();
		}
	}

}

       是不是看着清爽很多。

 

 

     用jdk类库简化代码

     首先来看一篇网友的文章。

     http://www.cnblogs.com/jackyuj/archive/2010/11/24/1886553.html

     文中的BlockingQueue是否看着就像是上文中Repository的一个封装实现? 有了这个工具类,对于一些简  单场景就不用重复造轮子了。限于篇幅,就先不在这里贴代码了。若有兴趣可以参考以下链接。

    http://yanxuxin.iteye.com/blog/583645

 

 

     总结

      消费者生产者模式可细化的内容不止如此,包括读写锁分离以此提高性能、生产速度和消费速度相匹配等问题。这部分内容以后和JDK1.5新出现的Lock对象以及部分BlockingQueue源码分析一起来说明。

   

         

  • 大小: 12.1 KB
分享到:
评论

相关推荐

    java多线程编程总结

    Java线程:并发协作-生产者消费者模型 Java线程:并发协作-死锁 Java线程:volatile关键字 Java线程:新特征-线程池 Java线程:新特征-有返回值的线程 Java线程:新特征-锁(上) Java线程:新特征-锁(下) Java...

    学习Java线程之并发协作生产者消费者模型.pdf

    学习Java线程之并发协作生产者消费者模型.pdf

    Java多线程编程总结

    Java线程:并发协作-生产者消费者模型 Java线程:并发协作-死锁 Java线程:volatile关键字 Java线程:新特征-线程池 Java线程:新特征-有返回值的线程 Java线程:新特征-锁(上) Java线程:新特征-锁(下) ...

    java多线程笔记

    Java线程:概念与原理 2 一、操作系统中线程和进程的概念 2 二、Java中的线程 3 三、Java中关于线程的名词解释...Java线程:并发协作-生产者消费者模型 52 Java线程:并发协作-死锁 55 Java线程:线程之间的数据传递 58

    Java 线程总结

    Java线程:并发协作-生产者消费者模型 Java线程:并发协作-死锁 Java线程:volatile关键字 Java线程:新特征-线程池 Java线程:新特征-有返回值的线程 Java线程:新特征-锁(上) Java线程:新特征-锁(下) Java...

    【资源免费下载】Java代码积累丨大话设计模式(Java实现版本)、线程协作

    生产者-消费者 设计模式参考《大话设计模式》 工厂简单模式 创造型模式 工厂方法模式 抽象工厂模式 原型模式 建造者模式 单例模式 结构型模式 队列模式 桥接模式 组合模式 装饰模式 外观模式 享元模式 代理模式 行为...

    操作系统课程设计——模拟生产者与消费者(java)

    生产者与消费者进程共享一个大小固定的缓冲区。其中,一个或多个生产者生产数据,并将生产的数据存入缓冲区,并有一个或多个消费者从缓冲区中取数据。 2、 系统设计: 系统的设计必须要体现进程之间的同步关系,...

    java并发之线程间通信协作.docx

    生产者如果不释放对临界资源的占用权,那么 消费者就无法消费队列中的商品 ,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起...

    JAVA作业二生产者消费者问题.docx

    作业二:生产者消费者问题。 要求: (1)生产者与消费者均为独立的线程。 (2)生产者与消费者之间有条不紊的协作进行数据的生产和消费。

    java实验实验报告 add 实验一:多线程/2.Runnable接口. 实验八:JDBC数据库编程二-存储过程

    重命名 实验三多:线程同步与协作:生产者与消费者 为 实验三:多线程同步与协作:生产者与消费者 实验九:使用jdbc存取大文本 add 实验九:使用jdbc存取大文本. 实验二:多线程打字游戏 add 实验二:多线程...

    Java线程指南

    Java线程指南 线程安全与不安全 线程同步synchronized和volatile 线程协作-生产者/消费者模式 Timer和TimerTask 线程池 Callable和Future 锁对象Lock-同步问题更完美的处理方式 Condition-线程通信更高效的方式

    Thread_study04:多线程_线程协作_生产者消费者模式,管程法,信号灯法,定时调度,单例模式,Threadlocal,可重入锁,CAS

    Thread_study04:多线程_线程协作_生产者消费者模式,管程法,信号灯法,定时调度,单例模式,Threadlocal,可重入锁,CAS

    Java多线程的等待唤醒机制代码演示通俗易懂

    生产者和消费者是一个十分经典的多线程协作模式 **常见方法:** - void wait() 当前线程等待,直到被其他线程唤醒 - void notify() 随机唤醒单个线程 - void notifyAll() 唤醒所有线程

    个人总结的深入java多线程开发

    1)演示简单的消费者和生产者的例子: 17 2)管道的读写流处理方式 19 3)重要的演示死锁的问题—哲学家就餐问题 20 4)终止多线程程序的两种方式(轮询访问变量和interrupt方法) 23 四Concurrent包详解 25 1)Executor...

    java高手真经 (UML建模+设计模式+面向服务架构) 卷8

    状态图——Java线程类Thread uml/activity_bug.mdl //06.活动图——Bug管理系统 uml/activity_atm.mdl //06.活动图——ATM机存取款 uml/activity_thread.mdl //06.活动图——Java线程类Thread uml/sequence_bug.mdl ...

    java高手真经 (UML建模+设计模式+面向服务架构) 卷1

    状态图——Java线程类Thread uml/activity_bug.mdl //06.活动图——Bug管理系统 uml/activity_atm.mdl //06.活动图——ATM机存取款 uml/activity_thread.mdl //06.活动图——Java线程类Thread uml/sequence_...

    java高手真经 (UML建模+设计模式+面向服务架构) 卷9

    状态图——Java线程类Thread uml/activity_bug.mdl //06.活动图——Bug管理系统 uml/activity_atm.mdl //06.活动图——ATM机存取款 uml/activity_thread.mdl //06.活动图——Java线程类Thread uml/sequence_bug.mdl ...

    java高手真经 (UML建模+设计模式+面向服务架构) 卷3

    状态图——Java线程类Thread uml/activity_bug.mdl //06.活动图——Bug管理系统 uml/activity_atm.mdl //06.活动图——ATM机存取款 uml/activity_thread.mdl //06.活动图——Java线程类Thread uml/sequence_...

Global site tag (gtag.js) - Google Analytics