Java中使用Files.walkFileTree遍历文件时如何暂停、恢复或降速?
问题解答
现有实现是否支持暂停?
当前你给出的WalkFilePaths类默认不支持暂停遍历,原因是Files.walkFileTree是同步遍历逻辑,SimpleFileVisitor的所有回调默认直接返回FileVisitResult.CONTINUE,没有加任何状态判断的控制逻辑,启动后会一次性跑完整个路径的遍历。
解决方案
方案1:自适应暂停/恢复(推荐,完全匹配需求)
你可以通过增加待入库队列长度检测逻辑,实现遍历自动暂停、自动恢复,无需人工干预:
- 先在
FileDataManager类中新增两个方法:getPendingCount():返回当前待入库的HashMap中元素总数awaitIfOverLoad():内部用等待通知机制,当待入库元素超过你设置的阈值(比如10000,为批量入库阈值的2倍)时阻塞调用线程,待入库线程批量写完数据、清空已入库元素后,调用notifyAll()唤醒阻塞的遍历线程。
- 改造
FolderWalker的visitFile方法,提交任务前先做过载判断:
@Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { if(attrs.isRegularFile()) { // 新增过载判断:待入库数据过多时阻塞,等入库进度追上再继续 fileDataManager.awaitIfOverLoad(); executor.execute(fileDataManager.addFileMap(new FileDataModel(path.toFile(), attrs.creationTime().toInstant(), attrs.lastAccessTime().toInstant()))); } return FileVisitResult.CONTINUE; }
方案2:线程池天然限流(改造成本最低)
不需要修改WalkFilePaths的代码,只需要调整初始化ThreadPoolExecutor的参数,利用线程池的有界队列+拒绝策略自动限制遍历速度:
// 线程池初始化示例 ThreadPoolExecutor executor = new ThreadPoolExecutor( // 核心线程数、最大线程数可根据你的服务器性能调整 4, 8, 60L, TimeUnit.SECONDS, // 用有界队列,容量可设置为批量入库阈值的2-3倍,比如15000 new ArrayBlockingQueue<>(15000), Executors.defaultThreadFactory(), // 核心配置:队列满时,提交任务的线程(就是你的文件遍历线程)自己执行提交的任务 // 变相让遍历线程暂停遍历,去处理入库逻辑,等队列有空位再继续遍历 new ThreadPoolExecutor.CallerRunsPolicy() );
这个方案的遍历速度会自动和入库速度匹配,不会出现数据堆积上百万的情况。
额外优化建议
- 你当前多线程操作HashMap存在并发安全问题,建议替换为
ConcurrentHashMap,或者在FileDataManager中操作HashMap的逻辑上加锁,避免数据丢失、死循环等异常。 - 批量入库时,建议先把HashMap中所有待入库元素一次性取出,再清空对应位置的记录,避免边遍历边删的多线程冲突。
内容的提问来源于stack exchange,提问作者Michael Sims
相关产品推荐
相关产品推荐

