我有一个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;
}
答案 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 进程,因为它们不共享相同的锁。