Java Watch Service - 等待修改完成

时间:2017-05-21 07:22:29

标签: java

我有一个Watcher,可以在听到更改时更新我的​​数据结构。但是,如果更改不是即时的(即,如果从另一个文件系统复制大文件,或者文件的大部分被修改),则数据结构会过早更新并引发错误。

如何修改我的代码,以便在调用最后一个updateData() 之后调用ENTRY_MODIFY,而不是在每个ENTRY_MODIFY之后调用{/ 1}}。

private static boolean processWatcherEvents () {
    WatchKey key;
    try {
        key = watcher.poll( 10, TimeUnit.MILLISECONDS );
    } catch ( InterruptedException e ) {
        return false;
    }

    Path directory = keys.get( key );
    if ( directory == null ) {
        return false;
    }

    for ( WatchEvent <?> event : key.pollEvents() ) {
        WatchEvent.Kind eventKind = event.kind();

        WatchEvent <Path> watchEvent = (WatchEvent<Path>)event;
        Path child = directory.resolve( watchEvent.context() );

        if ( eventKind == StandardWatchEventKinds.ENTRY_MODIFY ) {
            //TODO: Wait until modifications are "finished" before taking these actions. 
            if ( Files.isDirectory( child ) ) {
                updateData( child );
            }
        }

        boolean valid = key.reset();
        if ( !valid ) {
            keys.remove( key );
        }
    }

    return true;
}

3 个答案:

答案 0 :(得分:0)

使用时间戳可以解决您的问题吗? 创建一个地图,用于存储地图的时间戳。

Map<Path, Long> fileTimeStamps;

对于流程事件检查上次修改的时间戳。

 long oldFileModifiedTimeStamp = fileTimeStamps.get(filePath);
 long newFileModifiedTimeStamp = filePath.toFile().lastModified();
                if (newFileModifiedTimeStamp > oldFileModifiedTimeStamp)
                {
                    fileTimeStamps.remove(filePath);
                    onEventOccurred();
                    fileTimeStamps.put(filePath, filePath.toFile().lastModified());
                }

答案 1 :(得分:0)

我最终编写了一个线程,其中列出了我想要更新的内容,并延迟实际更新它们直到80毫秒过去。每当发生ENTRY_MODIFY事件时,它都会重置计数器。我认为这是一个很好的解决方案,但可能会更好?

@SuppressWarnings({ "rawtypes", "unchecked" })
private static boolean processWatcherEvents () {
    WatchKey key;
    try {
        key = watcher.poll( 10, TimeUnit.MILLISECONDS );
    } catch ( InterruptedException e ) {
        return false;
    }

    Path directory = keys.get( key );
    if ( directory == null ) {
        return false;
    }

    for ( WatchEvent <?> event : key.pollEvents() ) {
        WatchEvent.Kind eventKind = event.kind();

        WatchEvent <Path> watchEvent = (WatchEvent<Path>)event;
        Path child = directory.resolve( watchEvent.context() );

        if ( eventKind == StandardWatchEventKinds.ENTRY_CREATE ) {
            if ( Files.isDirectory( child ) ) {
                loadMe.add( child );
            } else {
                loadMe.add( child.getParent() );
            }

        } else if ( eventKind == StandardWatchEventKinds.ENTRY_DELETE ) {
            //Handled by removeMissingFiles(), can ignore. 

        } else if ( eventKind == StandardWatchEventKinds.ENTRY_MODIFY ) {
            System.out.println( "Modified: " + child.toString() ); //TODO: DD
            if ( Files.isDirectory( child ) ) {
                modifiedFileDelayedUpdater.addUpdateItem( child );
            } else {
                modifiedFileDelayedUpdater.addUpdateItem( child );
            }

        } else if ( eventKind == StandardWatchEventKinds.OVERFLOW ) {
            for ( Path path : musicSourcePaths ) {
                updateMe.add( path );
            }
        }

        boolean valid = key.reset();
        if ( !valid ) {
            keys.remove( key );
        }
    }

    return true;
}

...

class UpdaterThread extends Thread {
    public static final int DELAY_LENGTH_MS = 80; 
    public int counter = DELAY_LENGTH_MS;

    Vector <Path> updateItems = new Vector <Path> ();

    public void run() {
        while ( true ) {
            long sleepTime = 0;
            try { 
                long startSleepTime = System.currentTimeMillis();
                Thread.sleep ( 20 ); 
                sleepTime = System.currentTimeMillis() - startSleepTime;
            } catch ( InterruptedException e ) {} //TODO: Is this OK to do? Feels like a bad idea.  

            if ( counter > 0 ) {
                counter -= sleepTime;

            } else if ( updateItems.size() > 0 ) {
                Vector <Path> copyUpdateItems = new Vector<Path> ( updateItems );
                for ( Path path : copyUpdateItems ) {
                    Library.requestUpdate ( path );
                    updateItems.remove( path );

                }
            }
        }
    }

    public void addUpdateItem ( Path path ) {
        counter = DELAY_LENGTH_MS;
        if ( !updateItems.contains( path ) ) {
            updateItems.add ( path );
        }
    }
};

答案 2 :(得分:0)

正如@TT 建议的那样,你可以很容易地做到with file locks

当您收到事件时,对读写访问使用阻塞方法 lock()。因此操作是阻塞的,代码自动等待直到写操作完成。

FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
try (channel) {  // auto closable, uses channel.close() in finally block
  channel.lock();  // wait until file modifications are finished
  channel.read(...);  // now you can safely read the file
}

但是,这不适用于不同的 JVM 进程,因为它们不共享相同的锁。