使用Watch Service遇文件重复更新及重建同名文件报错求助
嘿,我之前也踩过Watch Service这俩坑,给你分享下亲测有效的解决思路和代码调整方案:
问题1:ENTRY_MODIFY事件被重复触发两次
原因分析
大多数编辑器(比如Word、Notepad++)保存文件时,不是直接修改原文件,而是走「创建临时文件→写入内容→删除原文件→重命名临时文件为原文件名」的流程,这会触发ENTRY_DELETE+ENTRY_CREATE,同时原文件的修改操作可能还会额外触发一次ENTRY_MODIFY;另外有些保存操作会分两步(写入文件内容+更新文件元数据,比如修改时间),也会导致两次ENTRY_MODIFY事件。
解决方案:添加时间去重逻辑
我们可以记录每个文件最后一次被处理的时间,当再次收到同一文件的ENTRY_MODIFY事件时,如果两次事件间隔很短(比如500ms),就忽略这次重复事件。
修改后的代码示例:
import java.nio.file.*; import java.util.HashMap; import java.util.Map; public class WatchServiceDemo { // 记录文件最后一次处理的时间戳 private static final Map<String, Long> lastProcessTime = new HashMap<>(); // 去重间隔(单位:毫秒) private static final long DEBOUNCE_TIME = 500; public static void main(String[] args) throws Exception { WatchService watchService = FileSystems.getDefault().newWatchService(); Path dir = Paths.get("D:\\Watson\\docs"); dir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE); boolean valid = true; do { WatchKey watchKey = watchService.take(); for (WatchEvent<?> event : watchKey.pollEvents()) { WatchEvent.Kind<?> kind = event.kind(); String fileName = event.context().toString(); Path filePath = dir.resolve(fileName); if (StandardWatchEventKinds.ENTRY_MODIFY.equals(kind)) { long currentTime = System.currentTimeMillis(); // 检查是否在去重间隔内,是则跳过 if (lastProcessTime.containsKey(fileName) && currentTime - lastProcessTime.get(fileName) < DEBOUNCE_TIME) { continue; } lastProcessTime.put(fileName, currentTime); // 这里写你的文件更新逻辑 System.out.println("处理文件修改:" + fileName); } else if (StandardWatchEventKinds.ENTRY_CREATE.equals(kind)) { System.out.println("处理文件创建:" + fileName); } else if (StandardWatchEventKinds.ENTRY_DELETE.equals(kind)) { System.out.println("处理文件删除:" + fileName); // 删除后移除时间记录 lastProcessTime.remove(fileName); } } valid = watchKey.reset(); } while (valid); } }
问题2:删除后重建同名文件提示文件被占用
原因分析
删除文件后,操作系统需要一小段时间来释放文件句柄、更新文件系统状态,这时立刻创建同名文件,就会遇到「文件被其他进程占用」的错误;另外如果你的代码中之前打开过该文件的流但没正确关闭,也会导致这个问题。
解决方案:等待文件完全删除后再创建
可以通过循环检查文件是否真的不存在了,再执行创建操作,中间加短暂睡眠避免占用CPU:
示例代码(删除后重建的逻辑):
public static void deleteAndRecreateFile(Path filePath) throws Exception { // 删除文件 Files.deleteIfExists(filePath); // 等待文件完全被删除 while (Files.exists(filePath)) { Thread.sleep(100); // 每次等待100ms } // 现在创建同名文件 Files.createFile(filePath); // 后续写入内容操作... }
另外一定要注意:所有打开文件的流必须在finally块中关闭,避免残留的文件引用导致系统占用:
// 正确示例:用try-with-resources自动关闭 try (FileInputStream fis = new FileInputStream(filePath)) { // ...读取操作 } catch (IOException e) { e.printStackTrace(); }
内容的提问来源于stack exchange,提问作者Manish Verma
相关产品推荐
相关产品推荐

