文本去重应单线程流式读取+concurrenthashmap查重+缓冲写入,避免多线程争抢io和内存爆炸;多文件可并行处理各文件任务,但每任务仍单线程。

直接用并发容器处理文本重复行并不合适——文本去重本质是顺序读写+全局查重,不是多线程争抢共享状态的场景。真正高效的做法是:**用单线程流式读取 + 线程安全的查重结构(如 ConcurrentHashMap)做快速判断,配合缓冲写入,避免锁竞争和内存爆炸**。
核心思路:不硬上“并发读”,而用“并发安全的查重+分块缓冲”
高并发文本通常指单个大文件(GB级)或一批小文件(成百上千个),但“高并发”在这里容易误导——实际瓶颈在IO和查重速度,而非CPU线程数。盲目开多线程读同一文件反而导致磁盘寻道加剧、缓存失效。
- 对单个大文件:采用单线程逐行读取,用 ConcurrentHashMap
存已见行(key为行内容,value可固定为true),O(1)判断是否重复 - 对多个小文件:可用 ExecutorService 提交每个文件的去重任务,每个任务内部仍是单线程流式处理,互不干扰
- 关键优化:启用行缓冲(如 BufferedReader 的 8192 字节 buffer),关闭自动 flush,累积一定行数(如1000行)再批量写入输出文件,减少IO次数
Java 实现示例(单文件流式去重)
以下代码兼顾安全性、内存可控性和执行效率,适用于日志、CSV、配置等纯文本:
- 使用 ConcurrentHashMap.newKeySet()(JDK 8+)替代传统 set,线程安全且无锁开销
- 跳过空行和纯空白行(.isBlank()),避免误判
- 保留原始顺序(首次出现的行留下,后续重复跳过)
- 自动关闭资源,支持 UTF-8 编码
慎用的“伪并发”陷阱
有些方案建议用 ParallelStream 或 fork/join 处理整行 list,这在大文件中极易触发 OOM:
- ParallelStream 会先把全部行加载进内存再分片,GB 文件 = GB 堆内存
- 多线程同时写同一个输出文件需加 synchronized 或 BlockingQueue,反而串行化并引入锁等待
- uniq 命令本身不并发,sort -u 是外部排序,依赖磁盘临时文件,不是真正的并发加速
超大文件(>10GB)的务实策略
当内存不足以缓存所有唯一行时,放弃“全内存查重”,改用外排+分段哈希:
- 按行计算哈希(如 String.hashCode() % 1000),将原文件拆成 1000 个小文件(相同哈希值的行进同个文件)
- 每个小文件单独用上述 ConcurrentHashMap 方案去重(此时每个文件仅几MB到几十MB)
- 合并所有去重后的小文件,再整体 sort -u(此时总量已大幅下降,排序快得多)











