监视目录时仅触发一次事件

2022-09-03 05:30:32

使用java.nio监视服务,我尝试监视一个目录及其所有子目录:

Files.walkFileTree(projectPath, new SimpleFileVisitor<Path>() {
   @Override
   public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        WatchKey key = dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
        watched.put(key, new WatchableDirectory(dir, projectPath));
        return FileVisitResult.CONTINUE;
    }
});

然后我等待事件:

executor.submit(new Runnable() {
        @Override
        public void run() {
            try {
                WatchKey key;
                while ((key = watcher.take()) != null) {
                    List<WatchEvent<?>> events = key.pollEvents();
                    WatchableDirectory watchableDirectory = watched.get(key);
                    for (WatchEvent<?> event : events) {
                         ....
                    }
             }
          ....
         }
    }

(watched是保存从键到有关目录的元数据的映射的Map

但是,仅触发给定目录的第一个事件。每当我更改目录中的另一个文件时,如果文件已被更改,则不会发生任何事情(我通过放置断点并期望 for 循环中的逻辑发生来验证这一点)。

但是,如果我修改另一个目录中的文件,那么一切都可以正常工作(同样,只是第一次)。

不会引发任何异常(有一个 catch 子句 ),并且循环显然继续运行。java.lang.Exception

我认为可能一旦使用,目录可能会被注销。因此,我添加了一行,以便在处理其文件后重新注册它。无效果。

Windows 7, Java 7.

任何想法为什么?


答案 1

别忘了打电话

key.reset();

在循环中完成使用它后。while

文档状态

否则,如果对象存在挂起事件,则此监视键将立即重新排队到监视服务。如果没有挂起的事件,则监视密钥将进入就绪状态,并将保持该状态,直到检测到事件或取消监视密钥。

监视密钥可由多个并发线程安全使用。如果有多个线程从监视服务检索信号键,则应注意确保仅在处理对象的事件后才调用 reset 方法。这可确保一个线程随时处理对象的事件。

因此,如果您不重置,就好像您的手表被禁用一样。

WatchKey#reset()返回一个布尔值,了解它是否有效。按照教程中的说明处理该情况。

马尔科强调:

处理完密钥的事件后,需要通过调用 reset 将密钥放回就绪状态。如果此方法返回 false,则该密钥不再有效,循环可以退出。此步骤非常重要。如果无法调用 reset,则此项将不会再接收任何事件。


答案 2

推荐