Consumer本身不能异步写入硬盘,需结合线程池、分批查询与EasyExcel/POI实现:以Consumer<List<T>>为回调入口封装写入逻辑,分页查数据避免OOM,用CompletableFuture异步提交至IO线程池,通过临时文件或SXSSFWorkbook落盘。

Java中利用Consumer本身不能直接实现“异步写入硬盘”,它只是一个函数式接口,用于消费数据(即接收并处理数据),不涉及线程调度或I/O控制。真正实现分批 + 异步 + Excel写入,需要结合Consumer作为处理逻辑的抽象入口,再配合线程池、流式分块、以及像Apache POI或EasyExcel这类支持分Sheet/分文件/临时缓存的写入方案。
1. Consumer作为分批处理的统一回调入口
把Consumer<List<T>>作为每一批数据的写入行为封装,解耦“取数据”和“写Excel”的逻辑:
- 从数据库分页查出1000条记录 → 交给Consumer处理
- Consumer内部调用EasyExcel的
write()或POI的Workbook.write()写到临时文件或响应流 - 多个批次可复用同一个Consumer实例(状态无关),便于测试和替换实现
2. 分批:避免OOM,用Stream + limit/skip或MyBatis分页
不要一次性加载全部数据到内存。推荐方式:
- 使用MyBatis-Plus的
IPage或JDBC分页查询,每次查500~2000条 - 用
IntStream.iterate生成页码,提交到线程池 - 注意:EasyExcel的
ExcelWriter不支持跨批次追加到同一文件(除非用fill()或自定义WriteHandler),所以通常一个批次写一个独立文件(如report_001.xlsx)或一个Sheet
3. 异步:用CompletableFuture + 自定义线程池控制并发
避免阻塞主线程,同时防止过多并发压垮磁盘或JVM:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
立即学习“Java免费学习笔记(深入)”;
- 创建专用IO线程池:
Executors.newFixedThreadPool(4, r -> new Thread(r, "excel-write-pool")) - 每个批次包装为
CompletableFuture.runAsync(() -> writeBatch(data), pool) - 用
CompletableFuture.allOf(...).join()等待全部完成,再合并或通知用户 - 注意:POI的
XSSFWorkbook不是线程安全的,每个批次必须新建Workbook;EasyExcel默认线程安全(但ExcelWriter实例不能复用)
4. 写入硬盘:避免临时内存堆积,善用SAX模式与临时文件
大批量导出的核心瓶颈常在内存和磁盘IO,而非CPU:
- 用EasyExcel的
write(...).file(outputFile)直接落盘,不走ServletResponse - 开启EasyExcel的
AutoCloseStream = true,自动关闭OutputStream - 对超大场景(>10万行),用
SXSSFWorkbook(POI)或EasyExcel的syncWrite+temporaryFolder参数,启用磁盘缓冲 - Linux下可考虑将临时目录挂载到tmpfs(内存盘),提升小文件写入速度(需权衡内存占用)
关键不在Consumer本身,而在于把它作为轻量级策略注入点,串联分页拉取、线程调度、流式写入三环节。Consumer让业务侧只需关心“这批数据怎么写”,不用管谁来调、何时调、在哪写。

















