SpringBoot中ArrayList新增元素时如何触发处理替代定时轮询
实现方案
不用轮询的轻量实现方案基于JDK阻塞队列+单线程消费即可完成,完全满足触发式执行、严格顺序处理的要求,不需要引入额外第三方依赖。
方案一:Spring + JDK原生实现(轻量场景首选)
核心逻辑是用阻塞队列存储待处理文件,启动一个常驻的单线程循环消费队列:
- 队列无元素时,消费线程自动阻塞挂起,不占用CPU资源,完全没有轮询开销
- 新元素入队时会自动唤醒消费线程,立刻触发处理,延迟在毫秒级
- 单线程消费天然保证处理顺序,上一个文件处理完成后才会取下一个元素,严格满足顺序要求
代码实现
- 首先定义队列和单线程执行器的配置:
@Configuration public class FileProcessConfig { // 待处理文件阻塞队列,可根据业务预估容量设置队列大小 @Bean public BlockingQueue<FileMeta> fileProcessQueue() { return new LinkedBlockingQueue<>(1000); } // 单线程任务执行器,核心线程数固定为1,保证消费顺序 @Bean public ThreadPoolTaskExecutor fileProcessExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(1); executor.setMaxPoolSize(1); executor.setThreadNamePrefix("file-process-worker-"); executor.setWaitForTasksToCompleteOnShutdown(true); executor.setAwaitTerminationSeconds(60); executor.initialize(); return executor; } }
注:FileMeta是自定义的文件元信息类,存储文件路径、业务参数这类处理需要的字段即可
- 实现队列消费逻辑,服务启动时自动启动消费循环:
@Slf4j @Service public class FileProcessConsumer implements InitializingBean { @Resource private BlockingQueue<FileMeta> fileProcessQueue; @Resource private ThreadPoolTaskExecutor fileProcessExecutor; @Resource private FileCoreProcessService fileCoreProcessService; // 实际文件处理业务类 @Override public void afterPropertiesSet() { fileProcessExecutor.execute(() -> { while (!Thread.currentThread().isInterrupted()) { FileMeta currentFile = null; try { // take()方法会在队列为空时阻塞,有新元素入队立刻唤醒返回 currentFile = fileProcessQueue.take(); // 执行实际文件处理逻辑 fileCoreProcessService.doProcess(currentFile); } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.warn("文件处理线程被中断,停止消费"); break; } catch (Exception e) { // 单个文件处理异常不要打断整个消费循环,按业务需求做异常记录、重试即可 log.error("文件处理失败,文件信息:{}", currentFile, e); } } }); } }
- 修改文件接收接口,入队后立刻返回:
@RestController @RequestMapping("/file") public class FileReceiveController { @Resource private BlockingQueue<FileMeta> fileProcessQueue; @PostMapping("/upload") public ResponseEntity<String> receiveFile(MultipartFile file) { // 先把文件持久化到磁盘/临时存储,同时持久化待处理记录,避免服务重启丢任务 FileMeta fileMeta = persistUploadedFile(file); // 元素入队 fileProcessQueue.offer(fileMeta); return ResponseEntity.ok("元素已加入处理列表"); } }
方案二:高可靠场景选型
如果业务对任务可靠性要求高,需要服务重启不丢任务、支持失败重试、死信队列、任务监控等能力,可以直接用成熟的消息队列组件实现:
- 轻量可嵌入:可以用嵌入式消息队列,和Spring生态无缝集成
- 已有中间件:如果服务已经部署了RocketMQ、RabbitMQ这类消息中间件,直接用顺序消息模式,配置单线程消费即可,同样满足触发式执行、严格顺序的要求
注意事项
- 不要直接把文件内容存在内存队列里,队列里只存文件元信息,实际文件先落盘再入队,避免OOM
- 单文件处理的异常一定要捕获,避免单个任务失败导致整个消费线程终止
- 如果需要优雅停机,给线程池配置
waitForTasksToCompleteOnShutdown参数,停机时等待队列中剩余任务处理完成再关闭进程
内容的提问来源于stack exchange,提问作者Inazense
相关产品推荐
相关产品推荐

