一、WatchService的坑
虽然WatchService是一种极为方便的Java API,但是在使用中也存在一些坑。首先,操作系统的缓存机制可能会导致WatchService监听不到某些事件。其次,WatchService不支持监听远程目录的变化。最后,WatchService可能会轮询事件导致CPU占用率过高。
二、WatchService不能监听远程目录
由于WatchService是基于本地文件系统实现的,因此它不能监听远程目录的变化。如果需要监听远程目录的变化,可以考虑使用SSH、SCP或者FTP等协议实现。
三、WatchService原理
WatchService的原理是通过JNI层来向操作系统注册文件事件,然后通过回调函数将事件传递给Java应用层。当WatchService监控到文件系统上的事件时,会将该事件加入WatchService的事件队列中,然后等待Java应用层调用take()方法来获取事件。
四、WatchService用户组
在使用WatchService时,可以通过创建WatchKey对象并指定其用户组来区分不同的WatchService监听器。例如:
WatchService watchService = FileSystems.getDefault().newWatchService();
Path path = Paths.get("/path/to/listen");
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY).userData(new Object());
这样就可以在触发事件时通过WatchKey的userData()方法来确定事件是由哪个监听器触发的。
五、WatchService监听不到
在使用WatchService时,可能会出现监听不到某些事件的情况。有以下几种可能的原因:
1、操作系统缓存机制导致WatchService无法监听到某些事件。
2、WatchService内部事件队列满了,还没有被Java层消费。
3、监听器注册的文件没有读权限。
4、某些事件被同时触发,但是WatchService只能获取到一次事件。
5、操作系统或者JVM内存资源不足。
六、WatchService获取全路径
在使用WatchService时,获取文件变化的全路径非常重要。可以通过如下方式获取:
WatchKey watchKey = watchService.take();
for (WatchEvent<?> event : watchKey.pollEvents()) {
Path path = ((WatchEvent<Path>) event).context();
Path fullpath = ((Path) watchKey.watchable()).resolve(path); // 获取全路径
...
}
七、WatchService的modify
在使用WatchService监听文件的modify事件时,可能会出现修改多次但只监听到一次的情况。这是因为操作系统会在某个时间段内合并多个修改事件。可以通过在代码中加入时间延迟的方式来解决这个问题。
private long lastModifiedTime = 0L;
private WatchKey watchKey;
while (true) {
try {
watchKey = watchService.take();
for (WatchEvent<?> event : watchKey.pollEvents()) {
Path path = ((WatchEvent<Path>)event).context();
Path fullPath = ((Path) watchKey.watchable()).resolve(path);
if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
long currentModifiedTime = Files.getLastModifiedTime(fullPath).toMillis();
if (currentModifiedTime - lastModifiedTime > 1000) { // 延迟时间为1秒
lastModifiedTime = currentModifiedTime;
// 处理modify事件
}
}
}
watchKey.reset();
} catch (Throwable e) {
e.printStackTrace();
}
}
八、WatchService无法监听挂载目录
如果需要监听挂载目录的变化,可以考虑使用JavaNativeAccess(JNA)实现Native监听器,参考代码如下:
interface CLibrary extends Library {
public class StructStat extends Structure {
public long st_ino;
public long st_mode;
public long st_nlink;
...
}
CLibrary INSTANCE = (CLibrary) Native.loadLibrary("c", CLibrary.class);
int statvfs(String path, StructStatvfs buf);
int stat(String path, StructStat buf);
}
// 监听挂载目录
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
CLibrary.StructStat buf = new CLibrary.StructStat();
int res = CLibrary.INSTANCE.stat(path, buf);
if (res == 0) {
long fsUid = buf.st_dev;
if (fsUid == expectedFsUid) { // 监听目标文件系统
// 监听文件变化
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
原创文章,作者:OPIJ,如若转载,请注明出处:https://www.506064.com/n/135745.html