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源码分析一起来说明。

   

         

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值