java学习笔记之多线程生产者与消费者

  生产者-消费者问题是一个经典的线程同步问题。可以理解为:生产者生产出一个产品,将其放入缓冲区,消费者从缓冲区取走产品之后,生产者继续生产产品。注意的是,生产者必须等待消费者取走产品之后才继续生产,消费者也必须在缓冲区有产品是才消费。

一、首先,最简单的模型就是单生产者,单消费者,单次生产消费


public class Resource {
	private String name;
	private boolean flag;
	private int count=1;
	public void setResource(String name){
		this.name = name + "---" +count;
		count++;
		System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
	}
	public void getResource(){
		System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
	}
}
public class Producer implements Runnable {
	private Resource rec;  //这一步是为了保证生产者和消费者操作的资源是一样的
	public Producer(Resource rec){
		this.rec=rec;
	}
	public void run(){
	    rec.setResource("面包");
	}
}
public class Customer implements Runnable{
	private Resource res;
	Customer(Resource res){
		this.res=res;
	}
	public void run(){
	    res.getResource();
	}
}
public class ProducerCustomer {
	public static void main(String[] args) {
		Resource res = new Resource();
		Producer pro = new Producer(res);
		Customer cus = new Customer(res);
		Thread th1 = new Thread(pro);
		Thread th2 = new Thread(cus);
		th1.start();
		th2.start();		
	}
}

结果是:

Thread-0生产者面包---1
Thread-1消费者面包---1

意思就是生产者生产一个产品,消费者就消费一个产品


二、单生产者单消费者多次生产消费

   那么该如何实现多生产者多消费者呢?可以在生产者和消费者的run函数里面加上while(true)的判断,但是这样就会产生消费者重复消费或者生产者重复生产的问题,如下图。原因就是多线程的不安全性,那么就可以用同步来解决该问题。


   当时当给生产者和消费者加上同步之后,publicsynchronizedvoid setResource(String name);public synchronized void getResource(),会发现又产生了生产者持续多生产和消费者持续多消费的问题,如下图


   该问题是由于生产者或消费者不断拿到锁,可以不断生产或消费。那么可以在拿到锁之后,加上判断的一步,如果没有产品才生产,有产品才消费。这就可以引入等待唤醒机制。

wait():该方法可以让线程处于冻结状态,并将线程先临时存储到线程池
notify():唤醒指定线程池中的任意一个线程
notifyAll(): 唤醒指定线程池中的所有线程
注意:1)这些方法必须用在同步中,因为它们是同来操作同步锁上的线程状态的。
    2)在使用这些方法时,必须标识它们所属于的锁,标识的方式就是:锁对象.wait()/锁对象.notify/锁对象.notifyAll,相同锁的notify()可以获取相同锁的wait()。(wait方法等在API中查看的时候,它是属于object的方法的)

完整程序如下:


public class Resource {
	private String name;
	private boolean flag;
	private int count=1;
	public synchronized void setResource(String name){
		if(flag){
			try{
				wait(); //查阅API可以知道,wait方法需要抛出InterruptedException异常,在该段代码中,wait方法对应的锁是默认的this,可以不写
			}
			catch(InterruptedException e){
				
			}
		}
		this.name = name + "---" +count;
		count++;
		System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
		flag = true;
		notify();
	}
	public synchronized void getResource(){
		if(!flag){  //在生产者或消费者拿到锁之后进行判断,是否是继续生产消费还是进入等待状态。
			try{
				wait();
			}
			catch(InterruptedException e){				
			}
		} 
		System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
		flag=false;
		notify();	
	}
}
public class Producer implements Runnable {
	private Resource rec;
	public Producer(Resource rec){
		this.rec=rec;
	}
	public void run(){
		while(true) 
	    rec.setResource("面包");
	}
}
public class Customer implements Runnable{
	private Resource res;
	Customer(Resource res){
		this.res=res;
	}
	public void run(){
		while(true)
	    res.getResource();
	}
}
public class ProducerCustomer {

	public static void main(String[] args) {
		Resource res = new Resource();
		Producer pro = new Producer(res);
		Customer cus = new Customer(res);
		Thread th1 = new Thread(pro);
		Thread th2 = new Thread(cus);
		th1.start();
		th2.start();
		
	}

}



结果如下图



三、多生产者多消费者多次生产消费

       首先,肯定是要开启多个线程来产生多个生产者和多个消费者,这样有可能会产生被唤醒的线程没有再次判断标记就开始工作,导致重复生产和重复消费的问题。所以可以把if 判断改为while判断,同时在上述代码中的notify()要改成notifyAll(),如果不改的话,会出现程序在运行过程中,所有线程都处于冻结状态,也就是死锁。(如何理解--->本方线程在唤醒时,又一次唤醒了本方线程。而本方线程循环判断标记时,又一次继续等待,导致所有线程都处于冻结状态)

       完整代码如下:


public class Resource {
	private String name;
	private boolean flag;
	private int count=1;
	public synchronized void setResource(String name){
		while(flag){ //循环判标记
			try{
				wait();//wait()方法
			}
			catch(InterruptedException e){
				
			}
		}
		this.name = name + "---" +count;
		count++;
		System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
		flag = true;
		notifyAll();
	}
	public synchronized void getResource(){
		while(!flag){
			try{
				wait();
			}
			catch(InterruptedException e){				
			}
		} 
		System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
		flag=false;
		notifyAll();	
	}
}
public class Producer implements Runnable {
	private Resource rec;
	public Producer(Resource rec){
		this.rec=rec;
	}
	public void run(){
		while(true)
	    rec.setResource("面包");
	}
}
public class Customer implements Runnable{
	private Resource res;
	Customer(Resource res){
		this.res=res;
	}
	public void run(){
		while(true)
	    res.getResource();
	}
}
public class ProducerCustomer {
	public static void main(String[] args) {
		Resource res = new Resource();
		Producer pro = new Producer(res);
		Customer cus = new Customer(res);
		Thread th0 = new Thread(pro); //开启多个线程
		Thread th1 = new Thread(pro);
		Thread th2 = new Thread(cus);
		Thread th3 = new Thread(cus);
		th0.start();
		th1.start();
		th2.start();
		th3.start();
	}
}


结果是


最后,可以发现,这个程序的效率较低,因为它在不断唤醒所有线程,并循环判断标记。但是实现了多生产和多消费。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值