You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot中ArrayList新增元素时如何触发处理替代定时轮询

实现方案

不用轮询的轻量实现方案基于JDK阻塞队列+单线程消费即可完成,完全满足触发式执行、严格顺序处理的要求,不需要引入额外第三方依赖。

方案一:Spring + JDK原生实现(轻量场景首选)

核心逻辑是用阻塞队列存储待处理文件,启动一个常驻的单线程循环消费队列:

  • 队列无元素时,消费线程自动阻塞挂起,不占用CPU资源,完全没有轮询开销
  • 新元素入队时会自动唤醒消费线程,立刻触发处理,延迟在毫秒级
  • 单线程消费天然保证处理顺序,上一个文件处理完成后才会取下一个元素,严格满足顺序要求

代码实现

  1. 首先定义队列和单线程执行器的配置:
@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是自定义的文件元信息类,存储文件路径、业务参数这类处理需要的字段即可

  1. 实现队列消费逻辑,服务启动时自动启动消费循环:
@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);
                }
            }
        });
    }
}
  1. 修改文件接收接口,入队后立刻返回:
@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 04:21:40