(四)生产者消费者模式

(一)生产者消费者模式原理:

在一个系统中,存在生产者和消费者两种角色,他们通过内存缓冲区进行通信,生产者生产消费者需要的资料,消费者把资料做成产品。生产消费者模式如下图:

(四)生产者消费者模式_第1张图片

(二)代码实现

生产者是一堆线程,消费者是另一堆线程,内存缓冲区可以使用List数组队列,数据类型只需要定义一个简单的类就好。最关键就是内存缓冲区为空的时候消费者必须等待,而内存缓冲区满的时候,生产者必须等待。其他时候可以是个动态平衡:
 
(四)生产者消费者模式_第2张图片
首先设置Market类:
其中变量有:Max(仓库最大值),List(用于存放数据)
方法有Producer(生产方法),Cost(消费方法)

public class Market{
   private int Max=20;
   private List<String>list=new ArrayList<String>();
   
   public synchronized void producer()throws InterruptedException{             //设置锁
        if(list.size()>=20){
           this.wait():                       //超过容量时,等待
          }
       System.out.println("***开始生产,现在数量为"+list.size()); 
       list.add("a");
       System.out.println("***生产后数量为"+list.size());
          
           this.notify();                 //唤醒线程
       }

  public synchronized void cost()throws InterruptedException{
              if(list.size()<0){
                  this.wait();                     //线程休眠
                  }
              
        System.out.println("开始消费,现在数量为:"+list.size());
        list.remove(0);
        System.out.println("消费后数量为:"+list.size());
           this.notify();                      //唤醒线程
        }
}

其次分别时Producer和Cost类,都要继承Thread类,重写run方法

public class Producer extends Thread{                //继承run方法
   private Market market;
   public Producer(Market market){
       this.market=market;
       }
    public void run(){
       while(true) {
	
			try {
				Thread.sleep(new Random().nextInt(500));     //设置时间延迟
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		try {
			market.production();                              //调用生产方法
		} catch (InterruptedException e) {             
			// TODO Auto-generated catch block
			e.printStackTrace();
		 }
	  }
	}

public class Cost extends Thread{                //继承run方法
   private Market market;
   public Cost(Market market){
       this.market=market;
       }
    public void run(){
       while(true) {
	
			try {
				Thread.sleep(new Random().nextInt(500));     //设置时间延迟
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		try {
			market.cost();                              //调用消费方法
		} catch (InterruptedException e) {             
			// TODO Auto-generated catch block
			e.printStackTrace();
		 }
	  }
	}


最后写出测试类,启动线程

public class Test{
   public static void main(String[]args){
           Market market=new Market();
		Producer p1=new Producer(market);
		Producer p2=new Producer(market);
		Producer p3=new Producer(market);
		
		Cost c1=new Cost(market);
		Cost c2=new Cost(market);
		
		
		
		p1.start();                //启动线程
		p2.start();
		p3.start();
		
		c1.start();
		c2.start();
      }
}

得到的运行结果为:
(四)生产者消费者模式_第3张图片
可知模拟的生产者消费者模式

(三)代码关键点分析:

1.锁问题:

对于Market中的Producer方法和Cost方法,要使用Synchronized关键字同步非静态方法,且要与wait()方法放在一起,使得在多个消费者生产者线程访问Market中的共享资源时不会发生线程安全问题,并且可以保证锁的时同一个对象

  public synchronized void producer()

  public synchronized void cost ()

2.线程的等待与重启

在生产时达到仓库的最大值后,要停止生产,即让生产的线程处于等待状态,此时需要调用wait()方法,


               if(list.size()>=20){
                  this.wait();                     //线程休眠
                  }

                this.notify();                 //唤醒线程

要用this来调用wait()方法,而不能用list,是因为被加锁的对象this在调用wait()和notify(),加锁的对象调用 wait() 方法后,线程进入等待状态,直到在加锁的对象上调用 notify() 方法来唤醒之前进入等待的线程

3.While循环放置在Producer和Cost类的run方法中:

使用while循环是原因:要保证线程可以一直运行下去:

   public void run(){
       while(true) {
	
			try {
				Thread.sleep(new Random().nextInt(500));     //设置时间延迟
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		try {
			market.cost();                              //调用消费方法
		} catch (InterruptedException e) {             
			// TODO Auto-generated catch block
			e.printStackTrace();
		 }
	  }

如果把while循环放在Market中,则睡眠的线程被唤醒之后有可能不满足while()中的条件判定,使得线程线程无法再调用Market中的producer方法,从而无法唤醒线程,因此要放在线程外

4.时间延迟问题:

若要模拟生产和消费的随机过程,还要设置时间延迟

  public void run(){
       while(true) {
	
			try {
				Thread.sleep(new Random().nextInt(500));     //设置时间延迟
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		try {
			market.cost();                              //调用消费方法
		} catch (InterruptedException e) {             
			// TODO Auto-generated catch block
			e.printStackTrace();
		 }
	  }

放在Producer类中的原因是只有放在run方法中,线程运行时才会具有随机性
否则会发生只有生产达到最大值后才会开始消费

你可能感兴趣的:(线程安全,eclipse,java)