一、 背景
在做大批量数据的导入操作时,为了提升性能,我们是否可以使用阻塞队列实现异步读取数据和处理数据呢?显然是可以这样操作的,我们可以使用主线程读取Excel数据,将数据放入阻塞队列中,同时新建一个子线程,对读取的Excel数据进行系列的逻辑处理,这样等于本来是一个人做的事,我们改成了两个人同时进行。再进一步思考,既然是异步生产和消费数据,我们是否可以使用多线程呢,把拆分成两个人改成拆分成10个人,5个人生产数据,5个人读取数据,这样是不是性能再次提升呢?接下来,我就使用阻塞队列来实现生产者和消费者模式。
二、代码实现
2.1 定义生产者
数据传输对象QueueDataDTO:
package concurrents.blockqueue.dto;
import lombok.Data;
/**
* @author hjf
* @date 2022-06-27 15:17
*
* 生产者、消费者生产消费的数据
*/
@Data
public class QueueDataDTO {
private String name;
}
生产者Producer:
package concurrents.blockqueue.producer;
import concurrents.blockqueue.dto.QueueDataDTO;
import lombok.SneakyThrows;
import java.util.concurrent.ArrayBlockingQueue;
/**
* @author hjf
* @date 2022-06-27 15:23
*
* 生产者
*/
public class Producer implements Runnable{
private final ArrayBlockingQueue<QueueDataDTO> dataQueue;
public Producer(ArrayBlockingQueue<QueueDataDTO> dataQueue) {
this.dataQueue = dataQueue;
}
@SneakyThrows
@Override
public void run() {
int count = 1;
while (count < 10){
QueueDataDTO dataDTO = new QueueDataDTO();
dataDTO.setName("第"+count+"数据");
count++;
Thread.sleep(400);
System.out.println("生产第"+count+"条数据");
dataQueue.add(dataDTO);
}
}
}
2.2 定义消费者
消费者Consumer:
package concurrents.blockqueue.consumer;
import concurrents.blockqueue.dto.QueueDataDTO;
import lombok.SneakyThrows;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* @author hjf
* @date 2022-06-27 15:12
*
* 消费者
*/
public class Consumer implements Runnable{
private final ArrayBlockingQueue<QueueDataDTO> dataQueue;
public Consumer(ArrayBlockingQueue dataQueue) {
this.dataQueue = dataQueue;
}
@SneakyThrows
@Override
public void run() {
QueueDataDTO data;
//poll从阻塞队列取出一个数据,限制取数时长为2秒,
// 这里做的目的限制阻塞时间最长2秒来结束子线程操作
while ((data = dataQueue.poll(2, TimeUnit.SECONDS)) != null){
System.out.println("消费者消费数据,数据为:"+ data.toString());
}
}
}
2.3 异步生产消费消息
主函数ConsumerAndProduceDemo示例:
package concurrents.blockqueue;
import concurrents.blockqueue.consumer.Consumer;
import concurrents.blockqueue.dto.QueueDataDTO;
import concurrents.blockqueue.producer.Producer;
import java.util.concurrent.ArrayBlockingQueue;
/**
* @author hjf
* @date 2022-06-27 14:40
*
* 阻塞队列实现生产者、消费者
* 生产者、消费者模式属于异步,发送方不需要知道接受方是否接受到消息,其只负责发送
* dubbo接口,属于同步消息,请求方需要根据发送方结果做一定的逻辑处理
*/
public class ConsumerAndProduceDemo {
private static ArrayBlockingQueue<QueueDataDTO> dataQueue = new ArrayBlockingQueue<QueueDataDTO>(1);
public static void main(String[] args) {
Consumer consumer = new Consumer(dataQueue);
Thread thread = new Thread(consumer);
thread.start();
Producer producer = new Producer(dataQueue);
producer.run();
}
}
2.4 结果展示
生产第1条数据
消费者消费数据,数据为:QueueDataDTO(name=第1数据)
生产第2条数据
消费者消费数据,数据为:QueueDataDTO(name=第2数据)
生产第3条数据
消费者消费数据,数据为:QueueDataDTO(name=第3数据)
生产第4条数据
消费者消费数据,数据为:QueueDataDTO(name=第4数据)
生产第5条数据
消费者消费数据,数据为:QueueDataDTO(name=第5数据)
生产第6条数据
消费者消费数据,数据为:QueueDataDTO(name=第6数据)
生产第7条数据
消费者消费数据,数据为:QueueDataDTO(name=第7数据)
生产第8条数据
消费者消费数据,数据为:QueueDataDTO(name=第8数据)
生产第9条数据
消费者消费数据,数据为:QueueDataDTO(name=第9数据)
三、总结
demo我们定义阻塞队列的长度为1,展示的是通过主线程来生产数据,同时新建一个子线程来消费数据,从打印的结果中也能看出,数据是顺序生产、顺序消费的,如果队列中数据达到定义的队列长度,生产者是阻塞的,同样,消费者消费数据,如果队列为空其也是阻塞的。
我们在日常开发中,如果数据量很大,性能要求高,我们可以改造成多线程异步生产消费,生产者和消费者都定义多个线程操作,同时扩大阻塞队列的长度,将长度扩大至我们定义的线程数量的3/4左右即可。