Java使用Files.walkFileTree删除目录时抛出NoSuchFileException的原因
问题结论
这个异常和Files.walkFileTree本身的线程安全性无关,属于遍历目录过程中文件系统发生并发变更导致的竞态问题。
从异常栈可以定位到,报错发生在FileTreeWalker尝试读取目标路径属性的阶段,此时遍历器记录的待访问路径已经被删除,因此抛出NoSuchFileException。
常见触发原因
- 其他线程/进程在删除逻辑执行期间,对目标目录树做了删除、重命名、移动等会改变路径存在性的操作:比如其他业务线程也在清理同目录文件、外部运维脚本手动清理目录、系统索引/杀毒进程临时操作文件等。
- 原有代码的异常捕获范围不全:你写的
NoSuchFileException捕获只覆盖了visitFile、postVisitDirectory回调里主动执行删除的逻辑,但walkFileTree的执行流程是先扫描目录拿到条目列表,再逐个读取条目属性、触发回调,两个步骤之间存在时间差,如果条目在扫描入列表后、被遍历访问前就被删除,遍历器本身读属性时就会直接抛异常,根本不会进入你写的catch逻辑。 - 仅其他线程读取目录内文件不会触发该异常,读操作不会改变文件的存在性,不会引发这类问题。
修复方案
需要补全遍历过程中访问文件失败的异常处理逻辑,同时简化删除逻辑的写法:
if (Files.exists(p)) { if (Files.isDirectory(p)) { Files.walkFileTree(p, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.deleteIfExists(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { // 遍历阶段发现文件已不存在直接跳过,其余异常正常抛出 if (exc instanceof NoSuchFileException) { return FileVisitResult.CONTINUE; } throw exc; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { if (e == null) { Files.deleteIfExists(dir); return FileVisitResult.CONTINUE; } // 目录已不存在的场景直接跳过,其余遍历异常正常抛出 if (e instanceof NoSuchFileException) { return FileVisitResult.CONTINUE; } throw e; } }); } }
如果业务要求目录删除过程不能被其他操作干扰,不要只靠异常兜底,可以在删除前先把目标目录重命名到一个无其他业务访问的临时路径,再执行遍历删除,从根源避免并发修改的问题。
内容的提问来源于stack exchange,提问作者SJZ
相关产品推荐
相关产品推荐

