基于RandomAccessFile的文件分片多线程读取的优化与拓展应用

一、引言

在上一篇文章(原文章链接)中,我们深入探讨了利用多线程分片读取文件内容的精妙实现。通过精心设计的代码逻辑,从文件按指定位置、大小划分分片,再借助多线程并发读取,最终无缝合并内容。然而,“金无足赤”,该方法在处理超大规模文件时,暴露出一个严峻的问题 —— 全量读取文件易导致内存溢出。当文件体量如巨兽般庞大,一股脑地将所有数据读入内存,就如同往一个有限容量的容器里拼命注水,内存占用必然超标,进而引发程序崩溃或运行卡顿,严重影响系统的稳定性与可用性。今天,我们站在这个关键节点上,继续深挖这套机制的潜力,探寻优化路径,解锁更多实用场景。

二、技术背景

在当今的数据处理领域,文件的规模呈爆炸式增长,动辄以 GB、TB 计。传统的单线程、全量读取文件方式在面对如此海量的数据时,显得力不从心。一方面,单线程无法充分利用现代多核处理器的强大并行计算能力,如同用一匹马拉一辆能由多匹马同时拉动的大车,效率极其低下;另一方面,如前文提及,将巨大文件一次性读入内存,无异于自找麻烦,内存资源很快就会被耗尽。

此时,RandomAccessFile 宛如一把利剑出鞘,为我们斩断困境。它允许我们灵活地在文件的任意位置进行读写操作,配合多线程分片读取策略,恰似给多核处理器的每个核心都分配了一项明确的任务。我们能够精准指定一次处理的数据量,并且依据数据量巧妙预估读取行数,确保每次读取的都是完整的行数据,避免数据碎片化带来的混乱。在读取的同时,迅速执行对应的处理方法,数据就像流水线上的产品,被高效加工处理,而不是堆积在内存仓库中。

三、代码实现解析

import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FileUtils {


    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(17);
        try {

            Instant startTime = Instant.now();

            LongAdder rowNum = new LongAdder();
            fragmentedReadFileRowLineAndExecute(executorService, "1.txt", 0, 70 * 1024 * 1024, 1024 * 1024, line -> {
                rowNum.increment();

                // TODO 具体业务实现
                System.out.println(line);
                return null;
            });

            // 打印耗时
            Instant endTimeReadAndParse = Instant.now();
            long durationReadAndParse = Duration.between(startTime, endTimeReadAndParse).toMillis();
            File file = new File("1.txt");
            System.out.println("文件大小:" + file.length() / (1024 * 1024 * 1024) + "GB,读取文件总耗时:" + durationReadAndParse + "毫秒,总行数:" + rowNum.sum());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executo

你可能感兴趣的:(Java爬坑之路,java)