黑马程序员_java多线程中的生产者与消费者

------ android培训java培训、期待与您交流! ----------

线程操作中有一个经典程序,就是生产者与消费者。


生产者生产出信息后将其放到一个区域中,消费者从中取出数据。由于涉及到线程运行的不稳定性,会存在以下两个问题:

1: 生产者线程刚向数据的存储空间添加了信息名称,还没有加入该信息的内容,程序就切换到消费者线程,消费者线程将信息的名称和上一个信息的内容联系到一起了。

2:生产者放了无数次数据,消费者才开始取数据,或者消费者取完一个数据,还没等到生产者放入新的数据,又重复取出已有数据。

代码如下:

class Info{	// 定义信息类
	private String name ;	 // 定义name属性
	private String content;		// 定义content属性
	public void setName(String name){
		this.name = name ;
	}
	public void setContent(String content){
		this.content = content ;
	}
	public String getName(){
		return this.name ;
	}
	public String getContent(){
		return this.content ;
	}
};
class Producer implements Runnable{	// 通过Runnable实现多线程
	private Info info = null ;		// 保存Info引用
	public Producer(Info info){
		this.info = info ;
	}
	public void run(){
		boolean flag = false ;	// 定义标记位
		for(int i=0;i<50;i++){
			if(flag){
				this.info.setName("苹果iPhone") ;	// 设置名称
				try{
					Thread.sleep(90) ;
				}catch(InterruptedException e){
					e.printStackTrace() ;
				}
				this.info.setContent("手机") ;	// 设置内容
				flag = false ;
			}else{
				this.info.setName("索尼walkman") ;	// 设置名称
				try{
					Thread.sleep(90) ;
				}catch(InterruptedException e){
					e.printStackTrace() ;
				}
				this.info.setContent("随身听") ;	// 设置内容
				flag = true ;
			}
		}
	}
};
class Consumer implements Runnable{
	private Info info = null ;
	public Consumer(Info info){
		this.info = info ;
	}
	public void run(){
		for(int i=0;i<50;i++){
			try{
				Thread.sleep(90) ;
			}catch(InterruptedException e){
				e.printStackTrace() ;
			}
			System.out.println(this.info.getName() + 
				" --> " + this.info.getContent()) ;
		}
	}
};
public class ThreadCaseDemo01{
	public static void main(String args[]){
		Info info = new Info();	// 实例化Info对象
		Producer pro = new Producer(info) ;	// 生产者
		Consumer con = new Consumer(info) ;	// 消费者
		new Thread(pro).start() ;
		new Thread(con).start() ;
	}
};



运行结果:

可以看到,两个问题已经出现。首先用同步来解决:

在class info中,可以将设置名称和姓名定义成一个同步方法:

class Info{	// 定义信息类
	private String name;	 // 定义name属性
	private String content  ;		// 定义content属性
	public synchronized void set(String name,String content){
		this.setName(name) ;	// 设置名称
		try{
			Thread.sleep(300) ;
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}
		this.setContent(content) ;	// 设置内容
	}
	public synchronized void get(){
		try{
			Thread.sleep(300) ;
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}
		System.out.println(this.getName() + 
			" --> " + this.getContent()) ;
	}
	public void setName(String name){
		this.name = name ;
	}
	public void setContent(String content){
		this.content = content ;
	}
	public String getName(){
		return this.name ;
	}
	public String getContent(){
		return this.content ;
	}
};

该类中,设置名称和内容用一个同步来实现,取得名称和内容也用一个同步实现;

相应也要修改Producer和Consumer;

class Producer implements Runnable{	// 通过Runnable实现多线程
	private Info info = null ;		// 保存Info引用
	public Producer(Info info){
		this.info = info ;
	}
	public void run(){
		boolean flag = false ;	// 定义标记位
		for(int i=0;i<50;i++){
			if(flag){
				this.info.set("苹果iPhone","手机") ;	// 设置名称
				flag = false ;
			}else{
				this.info.set("索尼walkman","随身听") ;	// 设置名称
				flag = true ;
			}
		}
	}
};
class Consumer implements Runnable{
	private Info info = null ;
	public Consumer(Info info){
		this.info = info ;
	}
	public void run(){
		for(int i=0;i<50;i++){
			this.info.get() ;
		}
	}
};


程序运行结果:


第一个问题解决。但在运行中可以发现每次启动时,只有一个生产者启动,即消费者重复生产。第二个生产者始终未启动。

生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者,通常采用线程间通信的方法解决该问题。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。

如果想让生产者不重复生产,消费者不重复取走,可以增加一个标志位,并假设标志位是布尔型变量,当标志位为true,则表示可以生产,不能取走。反之,标志位是false,表示可以取走,不能生产。

为了完成上述功能,我在info类中加入标志位,这样直接修改info类即可,不必修改生产者和消费者:代码如下:

class Info{	// 定义信息类
	private String name;	 // 定义name属性
	private String content;		// 定义content属性
	private int count=1;
	//private int count2=1;
	private boolean flag = false ;	// 设置标志位
	
	public synchronized void get(){
		while(flag){
			try{
				super.wait() ;
			}catch(InterruptedException e){
				e.printStackTrace() ;
			}
		}
		/*try{
			Thread.sleep(300) ;
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}*/
		System.out.println("Consumer..."+this.getName() + " --> " + this.getContent()+".....") ;
		flag  = true ;	// 改变标志位,表示可以生产
		this.notifyAll() ;
	}
	
	public synchronized void set(String name,String content){
		
		while(!flag){
			try{
				super.wait() ;
			}catch(InterruptedException e){
				e.printStackTrace() ;
			}
		}
		
		this.setName(name);	// 设置名称
		/*try{
			Thread.sleep(300) ;
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}*/
		this.setContent(content) ;	// 设置内容
		System.out.println("Producter..."+this.getName() + " --> "  + this.getContent()+".....");
		flag  = false ;	// 改变标志位,表示可以取走
		this.notifyAll() ;
	}
	
	
	
	public void setName(String name){
		this.name = name ;
	}
	public void setContent(String content){
		this.content = content+"-->"+count++ ;
	}
	public String getName(){
		return this.name ;
	}
	public String getContent(){
		return this.content ;
	}
};


运行结果:




问题解决。















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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值