1 基本概念
生产者-消费者模式是一个经典的并发设计模式,它为多线程之间的协作提供了良好的解决方案。在生产者-消费者设计模式中,有两类线程:若干生产者线程和若干消费者线程。二者共享一个内存缓冲区。生产者线程向共享缓冲区中添加请求或者资源,消费者线程负责从共享缓冲区取出请求处理或者取出资源使用。
生产者-消费者模式的核心是共享内存缓冲区,它作为生产者和消费者间的通信桥梁,避免了生产者和消费者的直接通信,实现两者之间的解耦。另外,由于缓冲区的存在,允许生产者和消费者在执行速度上存在差异,确保系统的正常运行。
2 涉及到的角色
(1)生产者:提交用户请求或者生产资源,添加到内存缓冲区。
(2)消费者:取出用户请求,然后处理,或者取出资源来使用。
(3)内存缓冲区:生产者和消费者交流信息的桥梁。缓存生产者提交的请求或者资源,供消费者使用。
(4)任务:生产者向内存缓冲区提交的数据结构。
3 代码实现
3.1不使用阻塞队列
(1)Plate.java:内存缓冲区
public class Plate {
private List<Cake> list;
/**
* 代表当前容量,即还可以再放多少块
*/
private int current;
/**
* 总共能放多少块蛋糕
*/
private int capacity;
public Plate(int size) {
// TODO Auto-generated constructor stub
list=new ArrayList<Cake>();
current=0;
capacity=size;
}
public synchronized void addCake(Cake cake) {
while (current>=capacity) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("生产蛋糕前的数量:"+current);
list.add(cake);
current++;
System.out.println("生产蛋糕后的数量:"+current);
notifyAll();
}
public synchronized Cake getCake() {
while(current<=0){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("消费蛋糕前的数量:"+current);
Cake cake=list.remove(--current);
System.out.println("消费蛋糕后的数量:"+current);
notifyAll();
return cake;
}
}
(2)Consumer.java:消费者
public class Consumer implements Runnable {
private Plate plate;
public Consumer(Plate plate) {
// TODO Auto-generated constructor stub
this.plate=plate;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("开始吃蛋糕----");
plate.getCake();
}
}
(3)Producer.java:生产者
public class Producer implements Runnable{
private Plate plate;
public Producer(Plate plate) {
// TODO Auto-generated constructor stub
this.plate=plate;
}
@Override
public void run() {
// TODO Auto-generated method stub
/**
* 这里限制了一个生产者只能生产一次蛋糕,而且只能生产一个,可以进行优化
*
*/
System.out.println("开始生产蛋糕");
plate.addCake(new Cake("cake"+Thread.currentThread().getId()));
}
}
(4)Cake.java:
public class Cake {
private String brand;
public Cake(String brand) {
// TODO Auto-generated constructor stub
this.brand=brand;
}
public String getBrand() {
return brand;
}
}
(5)Main.java:
public class Main {
public static void main(String[] args) {
Plate plate=new Plate(5);
for(int i=0;i<5;i++){
new Thread(new Producer(plate)).start();
}
for (int i = 0; i < 3; i++) {
new Thread(new Consumer(plate)).start();
}
}
}
3.2 使用阻塞队列
(1)Producer.java:
/**
* 使用阻塞队列
* @author 深蓝色
*
*/
public class Producer implements Runnable {
private BlockingQueue<Cake> queue;
public Producer(BlockingQueue<Cake> queue) {
// TODO Auto-generated constructor stub
this.queue=queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
/**
* 这里设置为一个生产者生产完一次蛋糕后,还可以继续生产
*/
while (true) {
Cake cake=new Cake("cake"+Thread.currentThread().getId());
try {
System.out.println("生产一块蛋糕");
/**
* 注意不要调用offer方法,因为当空间不足时,offer方法不会阻塞而是直接添加失败
*/
queue.put(cake);
/**
* 模拟耗时操作或者休息
*/
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
(2)Consumer.java:
public class Consumer implements Runnable{
private BlockingQueue<Cake> queue;
public Consumer(BlockingQueue<Cake> queue) {
// TODO Auto-generated constructor stub
this.queue=queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
/**
* 一个消费者可以吃多次蛋糕
*/
while (true) {
System.out.println("吃一块蛋糕----");
try {
queue.take();
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
(3)Main.java:
public class Main {
public static void main(String[] args) {
/**
* 这个盘子的容量设置为3
*/
BlockingQueue<Cake> queue=new LinkedBlockingDeque<Cake>(3);
for (int i = 0; i < 2; i++) {
new Thread(new Producer(queue)).start();
}
for (int i = 0; i < 5; i++) {
new Thread(new Consumer(queue)).start();
}
}
}
4 参考文献
1>.Java程序性能优化.葛一鸣 等著.
2>.Java并发编程实践.