除了Futrue模式和Master-Worker模式,生产者-消费者模式也是一个非常经典的多线程模式。在生产者-消费者模式中,通常有两类线程(即N+1个生产者和N+1个消费者的线程),生产者负责提交用户请求,消费者则负责进行具体的业务处理,生产者和消费者之间通过共享内存缓存区(MQ)进行通信。下面以一段小例子进行模拟:
Data:数据类,模拟生产者和消费者所操作的数据,设置两个属性:String id和String name,代码如下:
public class Data {
private String id;
private String name;
public Data(String id,String name) {
this.id = id;
this.name = name;
}
...
get/set方法
...
@Override
public String toString() {
return "Data [id=" + id + ", name=" + name + "]";
}
}
Provider:生产者,负责实例化新的data数据,包括一条BlockingQueue<Data>的队列引用:messageQueue(生产者将其实例化的数据存放到该队列里边);用于停止生产的开关:isRunning(一个boolean类型,使用volatile修饰的变量);ID生成器:count(new AtomicInteger(),在生产数据时使用);一个随机数:r(new Random(System.currentTimeMillis()),用于模拟生产者获取数据的时间)。具体代码如下:
public class Provider implements Runnable {
// 设置内存缓存区
BlockingQueue<Data> messageQueue;
//多线程开关
private volatile boolean isRunning = true;
//id生成器
private static AtomicInteger count = new AtomicInteger();
//随机对象
private static Random r = new Random(System.currentTimeMillis());
@SuppressWarnings("unused")
private Provider() {}
public Provider(BlockingQueue<Data> messageQueue) {
this.messageQueue = messageQueue;
}
@Override
public void run() {
while(isRunning) {
try {
//随机睡眠,表示获取数据所产生的时间
Thread.sleep(r.nextInt(5)*1000);
//累计获取的数据
int id = count.incrementAndGet();
//获取Data
Data data = new Data(Integer.toString(id),"数据:"+id);
System.err.println("当前线程(生产者)"+Thread.currentThread().getName()+",获取了数据,id为:"+id+",将其加载到公共缓存");
if(!this.messageQueue.offer(data,2,TimeUnit.SECONDS)){
System.err.println("提交缓冲区数据失败...");
}
}catch(Exception e) {
System.err.println(e.getMessage());
}
}
}
public void stop() {
this.isRunning = false;
}
}
Customer:消费者,用于对生产者实例化放入队列的数据的处理。包括一条BlockingQueue<Data>队列的引用:messageQueue。具体代码如下:
package _12_生产者消费者模式;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
public class Customer implements Runnable{
// 设置内存缓存区
BlockingQueue<Data> messageQueue;
@SuppressWarnings("unused")
private Customer() {}
public Customer(BlockingQueue<Data> messageQueue) {
this.messageQueue = messageQueue;
}
//随机对象
private static Random r = new Random();
@Override
public void run() {
while(true) {
try {
//获取数据
Data data = this.messageQueue.take();
//进行数据处理
Thread.sleep(r.nextInt(3)*1000);
System.err.println("当前线程(消费者):"+Thread.currentThread().getName()+"消费成功,消费数据Id为:"+data.getId());
}catch(Exception e) {
System.err.println(e.getMessage());
}
}
}
}
MainTest:测试类,用于实例化X个生产者和Y个消费者并使生产者生产2秒钟的数据,然后停止生产数据,消费者则对阻塞队列里边的数据进行处理。具体代码如下:
package _12_生产者消费者模式;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
public class MainTest {
public static void main(String[] args) {
// 设置内存缓存区
BlockingQueue<Data> messageQueue = new LinkedBlockingQueue<Data>(10);
//生产者
Provider provider_01 = new Provider(messageQueue);
Provider provider_02 = new Provider(messageQueue);
Provider provider_03 = new Provider(messageQueue);
Provider provider_04 = new Provider(messageQueue);
Provider provider_05 = new Provider(messageQueue);
Provider provider_06 = new Provider(messageQueue);
Provider provider_07 = new Provider(messageQueue);
Provider provider_08 = new Provider(messageQueue);
//消费者
Customer customer_01 = new Customer(messageQueue);
Customer customer_02 = new Customer(messageQueue);
//创建一个线程池,一个缓存的线程池,可创建无穷的线程,没有任务时不创建线程,空闲线程存活时间为默认(60秒)
ExecutorService cachePool = Executors.newCachedThreadPool();
cachePool.execute(provider_05);
cachePool.execute(provider_06);
cachePool.execute(provider_07);
cachePool.execute(provider_08);
cachePool.execute(provider_04);
cachePool.execute(customer_01);
cachePool.execute(provider_03);
cachePool.execute(customer_02);
cachePool.execute(provider_02);
cachePool.execute(provider_01);
try {
Thread.sleep(5*1000);
}catch(Exception e) {
System.err.println(e.getMessage());
}
provider_04.stop();
provider_03.stop();
provider_02.stop();
provider_01.stop();
provider_05.stop();
provider_06.stop();
provider_07.stop();
provider_08.stop();
try {
Thread.sleep(2*1000);
}catch(Exception e) {
System.err.println(e.getMessage());
}
/* cachePool.shutdown();
cachePool.shutdownNow();*/
}
}
运行效果如下:
当前线程(生产者)pool-1-thread-5,获取了数据,id为:4,将其加载到公共缓存
当前线程(生产者)pool-1-thread-3,获取了数据,id为:2,将其加载到公共缓存
当前线程(消费者):pool-1-thread-6消费成功,消费数据Id为:3
... //消费者和生产者运行中
当前线程(生产者)pool-1-thread-1,获取了数据,id为:26,将其加载到公共缓存
提交缓冲区数据失败... //由于生产者产生数据比消费者处理速度快,此时队列已满,数据不能放入
当前线程(消费者):pool-1-thread-8消费成功,消费数据Id为:15
//队列里边的数据被处理,产生空闲,生产者继续产生数据并放入阻塞队列
当前线程(生产者)pool-1-thread-2,获取了数据,id为:28,将其加载到公共缓存
当前线程(生产者)pool-1-thread-4,获取了数据,id为:30,将其加载到公共缓存
当前线程(消费者):pool-1-thread-8消费成功,消费数据Id为:18
...
//直至阻塞队列里边的数据处理完成