RandomAccessFile分片读取需手动计算offset和length,各线程用独立实例按字节区间读取并处理余数,合并时seek写入必须对齐且避免覆盖。

Java 中处理大文件的分片读取与分块合并,核心在于避免内存溢出、保证数据完整性,并合理利用并发提升吞吐。关键不是“多线程读同一个文件流”,而是将文件按偏移量切分成独立可读的逻辑块,每个块由单独线程处理,最后按顺序合并结果。
分片读取:基于 RandomAccessFile 或 MappedByteBuffer 切片
不能用普通的 BufferedReader 或 InputStream 直接多线程读——它们不支持随机定位且非线程安全。推荐两种方式:
-
RandomAccessFile + 偏移量计算:先获取文件总长度,按固定大小(如 4MB)划分区间,每个线程打开独立
RandomAccessFile实例,seek()到起始位置,读取指定字节数。注意边界处理(最后一块可能不足设定大小)。 -
MappedByteBuffer(适合超大文件):用
FileChannel.map()将文件区域映射为内存块。每个线程负责一个ByteBuffer子视图,直接读取无需拷贝。但要注意堆外内存管理和force()同步问题,且 Windows 下可能有映射限制。
并发读取控制:线程池 + Future 管理任务生命周期
用 ExecutorService 提交多个读取任务,每个任务返回该分片的原始字节或处理后的对象(如解析后的记录列表)。关键点:
- 使用
Future<byte></byte>或自定义结果类(含分片序号),确保后续能按顺序组装; - 设置合理的线程数(通常 ≤ CPU 核心数 × 1.5,IO 密集型可略高,但过多线程反而因上下文切换降低性能);
- 捕获每个分片的异常(如磁盘 I/O 错误),避免单个失败导致整体中断,可用
CompletableFuture实现失败重试或降级。
分块合并:按序写入或内存聚合后落盘
合并不是简单拼接字节数组,需考虑业务语义:
立即学习“Java免费学习笔记(深入)”;
- 若分片是完整记录(如每行一条 JSON),读取时需跳过跨块的不完整行(检查块末尾是否为换行符,必要时向前回溯补全);
- 合并结果建议用
FileOutputStream配合BufferedOutputStream顺序写入目标文件,避免把所有分片内容缓存在内存中; - 若必须内存合并(如需要统一排序/去重),用
ArrayList收集各分片结果,再调用Collections.sort()等操作,但务必评估内存是否承受得住。
完整性校验与错误恢复
大文件处理易受中断影响,需增强鲁棒性:
- 记录每个分片的处理状态(如用临时文件标记
part_001.done),重启时跳过已完成块; - 最终合并前,对比源文件和目标文件的
MessageDigest(如 SHA-256)哈希值,验证数据一致性; - 对关键字段(如记录总数、校验和)做摘要日志,便于快速定位哪一块出错。


















