Java 7 WatchService - 忽略同一事件的多次发生

2022-09-01 00:53:38

的 javadoc 说:StandardWatchEventKinds.ENTRY_MODIFY

已修改目录条目。当为此事件注册目录时,当观察到目录中的条目已被修改时,WatchKey 将排队。此事件的事件计数为 1 或更大。

当您通过编辑器编辑文件的内容时,它将同时修改日期(或其他元数据)和内容。因此,你会得到两个事件,但每个事件都有1(至少这是我所看到的)。ENTRY_MODIFYcount

我正在尝试使用以下代码监视手动更新(即通过命令行)的配置文件(以前已注册到)servers.cfgWatchServicevi

while(true) {
    watchKey = watchService.take(); // blocks

    for (WatchEvent<?> event : watchKey.pollEvents()) {
        WatchEvent<Path> watchEvent = (WatchEvent<Path>) event;
        WatchEvent.Kind<Path> kind = watchEvent.kind();

        System.out.println(watchEvent.context() + ", count: "+ watchEvent.count() + ", event: "+ watchEvent.kind());
        // prints (loop on the while twice)
        // servers.cfg, count: 1, event: ENTRY_MODIFY
        // servers.cfg, count: 1, event: ENTRY_MODIFY

        switch(kind.name()) {
            case "ENTRY_MODIFY":
                handleModify(watchEvent.context()); // reload configuration class
                break;
            case "ENTRY_DELETE":
                handleDelete(watchEvent.context()); // do something else
                break;              
        }
    }   

    watchKey.reset();       
}

由于您获得两个事件,因此上述操作将在只需要一次时重新加载配置两次。有没有办法忽略其中一个,假设可能有多个这样的事件?ENTRY_MODIFY

如果API具有这样的实用程序,那就更好了。(我有点不想检查每个事件之间的时间。我的代码中的所有处理程序方法都是同步的。WatchService

如果创建(复制/粘贴)文件从一个目录到监视目录,也会发生同样的情况。如何将这两者组合成一个事件?


答案 1

观察程序服务报告事件两次,因为基础文件更新了两次。一次用于内容,一次用于文件修改时间。这些事件发生在短时间内。要解决此问题,请在 或 和呼叫之间休眠。例如:poll()take()key.pollEvents()

@Override
@SuppressWarnings( "SleepWhileInLoop" )
public void run() {
  setListening( true );

  while( isListening() ) {
    try {
      final WatchKey key = getWatchService().take();
      final Path path = get( key );

      // Prevent receiving two separate ENTRY_MODIFY events: file modified
      // and timestamp updated. Instead, receive one ENTRY_MODIFY event
      // with two counts.
      Thread.sleep( 50 );

      for( final WatchEvent<?> event : key.pollEvents() ) {
        final Path changed = path.resolve( (Path)event.context() );

        if( event.kind() == ENTRY_MODIFY && isListening( changed ) ) {
          System.out.println( "Changed: " + changed );
        }
      }

      if( !key.reset() ) {
        ignore( path );
      }
    } catch( IOException | InterruptedException ex ) {
      // Stop eavesdropping.
      setListening( false );
    }
  }
}

呼叫有助于消除双重呼叫。延迟可能必须高达三秒。sleep()


答案 2

我遇到了类似的问题 - 我正在使用WatchService API来保持目录同步,但观察到在许多情况下,更新被执行了两次。我似乎已经通过检查文件上的时间戳解决了这个问题 - 这似乎屏蔽了第二个复制操作。(至少在Windows 7中 - 我不确定它是否能在其他操作系统中正常工作)

也许你可以使用类似的东西?存储文件中的时间戳并仅在时间戳更新时才重新加载?