介绍
本次案例的起因是,原本使用@Async注解进行异步调用的某个方法会占用spring线程池的资源,所以需要把这个方法变成同步,但是处理的函数得是异步的。具体的改进如下图所示。
这样做的话,处理线程就会一直在运行,不会占用spring的线程池资源。
并且在处理线程中可以写一些控制代码,比如为了防止无数据时cpu空转,可以写一个控制逻辑让线程在没有数据一段时间后休眠一定时间。在生产端也可以对数据进行控制,比如数据过多的时候,使用一个while(queen.size>maxSize)自旋,让数据生产速率变慢一点。
详细代码
最最开始先初始化一个阻塞队列
private BlockingQueue<FileOperation> blockingQueue = new ArrayBlockingQueue<FileOperation>(1024);
接下来,我主要介绍数据的生产者和消费者,具体的业务逻辑由于可以随意替换,就不做具体介绍了。
首先是消息的生产者,这个函数是给其他的业务类调用的,每次接收一条消息
@Override
public void insertintoGraph(FileOperation fileOperation){
// 将数据添加到阻塞队列中
// 如果阻塞队列中数据过多,就空转阻塞住,让消息队列慢一点添加数据
while(blockingQueue.size() > 1000);
try {
blockingQueue.put(fileOperation);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
比较重要的是进入这个函数后,首先需要进行一个检查,如果阻塞队列数据过多就要用一个空转阻塞调用者。
接着是消费者,消费者是一个独立的线程,在spring初始化bean的时候创建。可以在构造函数里创建,也可以使用@PostConstruct注解,告诉spring,这个函数是在构造函数执行后,初始化前执行。
该注解的具体执行流程可以看这篇文章@PostConstruct详解_一个喜欢健身的程序员的博客-CSDN博客_postconstruct
@PostConstruct
public void consume(){
new Thread(()->{
while(true) {
if (blockingQueue.size() > 0) {
try {
// 取出一条数据
FileOperation fileOperation = blockingQueue.take();
//System.out.println("消费数据:" + fileOperation);
// 对数据进行处理
FileGraphEntity fileGraphEntity = handle(fileOperation);
//System.out.println("处理完了:" + fileGraphEntity);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
其中handle()就是具体的处理函数。
总结
本文是我遇到的一个生产者-消费者模型的案例,希望可以帮到大家。