最近在学习Java多线程编程,当然少不了学习经典的 生产者——消费者 模型。
一、生产者——消费者
在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。
单单抽象出生产者和消费者,还够不上是生产者/消费者模式。该模式还需要有一个缓冲区处于生产者和消费者之间,作为一个中介。生产者把数据放入缓冲区,而消费者从缓冲区取出数据。大概的结构如下图。
为了不至于太抽象,我们举一个寄信的例子(虽说这年头寄信已经不时兴,但这个例子还是比较贴切的)。假设你要寄一封平信,大致过程如下:
1、你把信写好——相当于生产者制造数据
2、你把信放入邮筒——相当于生产者把数据放入缓冲区
3、邮递员把信从邮筒取出——相当于消费者把数据取出缓冲区
4、邮递员把信拿去邮局做相应的处理——相当于消费者处理数据
生产者——消费者模式经常用到,关于他的优点和怎么灵活运用,大家可以查阅相关资料,自己好好总结一下。
二、生产者——消费者的实现。
生产者——消费者的实现方法有以下几种:
1.用互斥锁实现,代码如下:
package multiThreads;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
/**
* @author Administrator
* 生产者——消费者
* 缓冲区为FIFO队列
*/
public class ConsumerProducer {
//定义缓冲池
private static Buffer buffer = new Buffer();
public static void main(String[] args) {
//创建含有两个线程的线程池用于执行从缓冲区读写的操作
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(new ProducerTask());
executor.execute(new ConsumerTask());
executor.shutdown();
}
//生产者任务类
private static class ProducerTask implements Runnable {
public void run() {
try {
int i = 1;
while (true) {
System.out.println("生产者写入 " + i);
//向缓存区写入整数
buffer.write(i++);
//线程休眠随机时间
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
//消费者任务类
private static class ConsumerTask implements Runnable {
public void run() {
try {
while (true) {
//从缓存区读取整数
System.out.println("\t\t\t消费者读取 " + buffer.read());
//线程休眠随机时间
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
//定义缓存区,用FIFO队列存取数据
private static class Buffer {
//缓存区容量
private static final int CAPACITY = 1;
//用LinkedList定义FIFO链队
private java.util.LinkedList<Integer> queue = new java.util.LinkedList<Integer>();
//定义互斥锁
private static Lock lock = new ReentrantLock();
//条件:缓存区非空
private static Condition notEmpty = lock.newCondition();
//条件:缓存区已满
private static Condition notFull = lock.newCondition();
//从缓存区读取整数
public void write(int value) {
lock.lock();
try {
while (queue.size() == CAPACITY) {
System.out.println("等待缓存区未满");
notFull.await();
}
//缓存区未满条件被唤醒之后,向队列添加
queue.offer(value);
//向缓存区非空条件发送信号
notEmpty.signal();
} catch (InterruptedException ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
}
@SuppressWarnings("finally")
public int read() {
int value = 0;
lock.lock();
try {
//当缓存区为空时候:等待非空条件
while (queue.isEmpty()) {
System.out.println("\t\t\t等待唤醒非空条件");
notEmpty.await();
}
//读取并删除数据
value = queue.remove();
//向缓存区未满条件发送信号
notFull.signal();
} catch (InterruptedException ex) {
ex.printStackTrace();
} finally {
lock.unlock();
return value;
}
}
}
}
二、采用阻塞FIFO队列实现。
package multiThreads;
import java.util.concurrent.*;
/**
* @author Administrator
* 采用阻塞队列实现生产者--消费者
*/
public class ConsumerProducerUsingBlockingQueue {
//定义阻塞FIFO队列
private static ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<Integer>(2);
//private static LinkedBlockingQueue<Integer> buffer = new LinkedBlockingQueue<Integer>(2);
public static void main(String[] args) {
//创建两个线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(new ProducerTask());
executor.execute(new ConsumerTask());
executor.shutdown();
}
//生产者任务:向缓冲池中添加整数
private static class ProducerTask implements Runnable {
public void run() {
try {
int i = 1;
while (true) {
System.out.println("生产者写入 " + i);
buffer.put(i++);
// 让线程休眠若干秒
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
//消费者任务:从缓冲池中读取并删除整数
private static class ConsumerTask implements Runnable {
public void run() {
try {
while (true) {
System.out.println("\t\t\t消费者读取 " + buffer.take());
//让线程休眠若干秒
Thread.sleep((int)(Math.random() * 10000));
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}