生产者-消费者是经典的多线程同步问题。
生产者消费者模式:生产者生产商品,消费者消费商品,同时生产者有生产上限,达到上限则停止生产,而在无产品的同时,消费者不能够消费。
例如:餐厅汉堡王和消费者,厨师(生产者)负责做汉堡(产品),最多可以存10个汉堡,当还有10个汉堡未售出的时候,厨师停止工作。消费者来买汉堡,消费过后,汉堡剩余9个,则厨师开始工作,当汉堡售尽时,消费者等待(堵塞),等待厨师生产。
实现原理: 通过锁的竞争进行资源的消耗或者生产。
实现方式:

  1. 通过synchronized加锁及wait() / notify()实现。
  2. 通过lock锁(await() / signal())实现
  3. 通过BlockQueue阻塞队列的方式实现

代码实现
一、通过synchronized加锁及wait() / notify()实现。
1、创建产品(资源)类

public class MyResource {
	private final int FULL = 10; //设置产品数量上限(即汉堡最多存多少个)  也可以说是缓冲大小
	private LinkedList<Object> list = new LinkedList<>();//定义一个链表,用来存储产品(汉堡),List Queue都可以
	
	public void produce() {
		synchronized(list) {
			while(list.size() >= FULL) {
			//当资源达到上限时,阻塞
				System.out.println("生产者"+Thread.currentThread().getName()+"产品上限");
				try {
					list.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//否则,进行生产
			list.add(new Object());
			System.out.println("生产者"+Thread.currentThread().getName()+"生产商品,现有"+list.size());
			list.notifyAll();
		}
	}
	
	public void consumer() {
		synchronized(list) {
			while(list.size() == 0) {
			//当资源为NULL时,阻塞
				System.out.println("消费者"+Thread.currentThread().getName()+"资源产品为NULL");
				try {
					list.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//否则消费产品
			list.remove();
			System.out.println("消费者"+Thread.currentThread().getName()+"消费商品,现有"+list.size());
			list.notifyAll();
		}
	}
	
}

2、创建生产者

public class Produce  implements Runnable{
	private MyResource resource;

	public Produce(){}

	public Produce(MyResource resource){
        this.resource = resource;
    }

	@Override
	public void run() {
		while (true) {
			try {
				Thread.sleep(1000);
				resource.produce();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

3、创建消费者

package com.zua.cp;

public class Consumer implements Runnable{
	
	private MyResource resource;
	
	public Consumer() {
		
	}
	
	public Consumer(MyResource resource) {
		this.resource = resource;
	}
	
	@Override
	public void run() {
		while(true){
		    try{
		        Thread.sleep(3000);
		        resource.consumer();
		    }catch (InterruptedException e){
		        e.printStackTrace();
		    }
		}
	}

}

4、主类测试

public class CPMain {
	public static void main(String[] args) {
        MyResource resource = new MyResource();
       new Thread(new Produce(resource)).start();
       new Thread(new Produce(resource)).start();
       new Thread(new Produce(resource)).start();

       new Thread(new Consumer(resource)).start();
       new Thread(new Consumer(resource)).start();
       new Thread(new Consumer(resource)).start();

    }
}

二、通过lock锁(await() / signal())实现
1、创建资源类

public class Storege {
	private final int FULL = 10; //资源上限
	volatile Integer i = 0; //借此观看取出的顺序,可以忽略
	private Queue<Integer> list = new LinkedList<>(); //用队列来实现
	private final Lock lock = new ReentrantLock(); //lock锁
	private final Condition full = lock.newCondition(); //满的变量,借此实现生产者阻塞和运行操作
	private final Condition empty = lock.newCondition();//空的变量,借此实现消费者阻塞和运行操作
	
	public void produce() {
		lock.lock();  //锁
		
		while (list.size() >= FULL) {
		//当资源满时,阻塞
			System.out.println("生产者" + Thread.currentThread().getName() + "资源满");
			try {
				full.await(); //用来阻塞,signal()唤醒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//否则存放资源
		list.add(i++);
		System.out.println("生产者" + Thread.currentThread().getName() + "生产商品,现有" + list.size());
		lock.unlock();
	}

	public void consumer() {
		lock.lock();
		while (list.size() == 0) {
		//资源null时,阻塞
			System.out.println("消费者" + Thread.currentThread().getName() + "资源NULL");
			try {
				empty.await();//用来阻塞,signal()唤醒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//否则,消费资源,rm可以看消费的顺序
		Integer rm = list.remove();
		System.out.println("消费者" + Thread.currentThread().getName() + "消费"+rm+"商品,现有" + list.size());
		full.signalAll();
		lock.unlock();
	}

2、生产者消费者主类测试同上

三、 通过BlockQueue阻塞队列的方式实现
BlockQueue是并发包下一个已经实现线程同步的队列。实现方式则是通过Lock锁及其await()和signal()方式。
再次用到的方法:

  1. put(Object obj):把obj加到BlockingQueue里,如果BlockQueue没有空间,则阻塞,直到BlockingQueue里面有空间再继续。
  2. take():取走BlockingQueue里第一位对象,若BlockingQueue为null,阻断进入等待状态直到BlockingQueue有新的数据被加入。

代码如下:使用内部类的方式实现

public class ConsumerAndProducer {
	public static void main(String[] args) {
		BlockingQueue<Object> queue = new ArrayBlockingQueue<>(10);//创建队列的同时,指定队列的大小,即资源的上限		
		ConsumerAndProducer3 cp = new ConsumerAndProducer3();
		new Thread(cp.new Producer(queue)).start();
		new Thread(cp.new Consumer(queue)).start();
		new Thread(cp.new Producer(queue)).start();
		new Thread(cp.new Consumer(queue)).start();
	}
	class Producer implements Runnable{
		BlockingQueue queue = null;
		
		public Producer(BlockingQueue queue) {
			this.queue = queue;
		}
		@Override
		public void run() {
			while(true) {
				 try {
					queue.put(new Object());
					System.out.println("生产者消费");
				 } catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	class Consumer implements Runnable{
		BlockingQueue queue = null;
		
		public Consumer(BlockingQueue queue) {
			this.queue = queue;
		}
		
		@Override
		public void run() {
			while(true) {
				 try {
					queue.take();
					System.out.println("消费者消费");
				 } catch (InterruptedException e) {
					e.printStackTrace();
				}
				 
			}
		}
	}
}