生产者-消费者是经典的多线程同步问题。
生产者消费者模式:生产者生产商品,消费者消费商品,同时生产者有生产上限,达到上限则停止生产,而在无产品的同时,消费者不能够消费。
例如:餐厅汉堡王和消费者,厨师(生产者)负责做汉堡(产品),最多可以存10个汉堡,当还有10个汉堡未售出的时候,厨师停止工作。消费者来买汉堡,消费过后,汉堡剩余9个,则厨师开始工作,当汉堡售尽时,消费者等待(堵塞),等待厨师生产。
实现原理: 通过锁的竞争进行资源的消耗或者生产。
实现方式:
- 通过synchronized加锁及wait() / notify()实现。
- 通过lock锁(await() / signal())实现
- 通过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()方式。
再次用到的方法:
- put(Object obj):把obj加到BlockingQueue里,如果BlockQueue没有空间,则阻塞,直到BlockingQueue里面有空间再继续。
- 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();
}
}
}
}
}