一、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/zh-hk/n/135745.html