JDK 之 NIO 2 WatchService、WatchKey(監控檔案變化)
阿新 • • 發佈:2018-12-30
JDK 之 NIO 2 WatchService、WatchKey(監控檔案變化)
JDK 規範目錄(https://www.cnblogs.com/binarylei/p/10200503.html)
一、WatchService、WatchKey 使用
具體詳見:https://blog.csdn.net/lirx_tech/article/details/51425364
public static void main(String[] args) throws Exception { // 1. 獲取檔案系統監控器,啟動一個後臺執行緒輪詢 WatchService watchService = FileSystems.getDefault().newWatchService(); // 2. 註冊要監聽的事件型別,檔案增、刪、改 Paths.get("C:\\Users\\len\\Desktop\\xdr").register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); while (true) { // 3. 獲取準備好的事件,pool() 立即返回、take() 阻塞 WatchKey watchKey = watchService.poll(2, TimeUnit.SECONDS); if (Objects.isNull(watchKey)) { continue; } // 4. 處理準備好的事件 List<WatchEvent<?>> watchEvents = watchKey.pollEvents(); for (WatchEvent<?> event : watchEvents) { if (event.kind().name().equals(StandardWatchEventKinds.ENTRY_CREATE.name())) { System.out.println("create: " + event.context()); } else if (event.kind().name().equals(StandardWatchEventKinds.ENTRY_MODIFY.name())) { System.out.println("modify: " + event.context()); } else if (event.kind().name().equals(StandardWatchEventKinds.ENTRY_DELETE.name())) { System.out.println("delete: " + event.context()); } } // 5. 重啟該執行緒,因為處理檔案可能是一個耗時的過程,因此呼叫 pool() 時需要阻塞監控器執行緒 boolean valid = watchKey.reset(); if (!valid) { break; } } }
二、原理
AbstractWatchService
實現 WatchService 介面WindowsWatchService
具體的實現,啟動 Poller 執行緒Poller
執行緒,輪詢指定的目錄
(1) AbstractWatchService
// 等待要處理的事件 signaled keys waiting to be dequeued private final LinkedBlockingDeque<WatchKey> pendingKeys = new LinkedBlockingDeque<WatchKey>(); @Override public final WatchKey poll(long timeout, TimeUnit unit) throws InterruptedException { checkOpen(); WatchKey key = pendingKeys.poll(timeout, unit); checkKey(key); return key; }
可以看到呼叫 poll 的時候直接從佇列中取 key,那就必然有一個執行緒往 pendingKeys 中塞資料。在 AbstractWatchService 中有一個 enqueueKey 方法往 pendingKeys 中塞資料。
final void enqueueKey(WatchKey key) {
pendingKeys.offer(key);
}
(2) WindowsWatchService
AbstractWatchService 有不同的實現,以 WindowsWatchService 為例。
WindowsWatchService(WindowsFileSystem fs) throws IOException { // create I/O completion port long port = 0L; try { port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0); } catch (WindowsException x) { throw new IOException(x.getMessage()); } this.poller = new Poller(fs, this, port); this.poller.start(); }
Poller 是 WindowsWatchService 的內部類,開啟了一個執行緒監控目錄。
(3) Poller
重點關注 Poller 中的 run 方法。
@Override
public void run() {
for (;;) {
CompletionStatus info;
try {
info = GetQueuedCompletionStatus(port);
} catch (WindowsException x) {
return;
}
WindowsWatchKey key = ck2key.get((int)info.completionKey());
if (key == null) {
continue;
}
boolean criticalError = false;
if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
} else if (errorCode != 0 && errorCode != ERROR_MORE_DATA) {
criticalError = true;
} else {
// 省略... (處理 error)
}
// 一切正常則 criticalError = true,此時將這個 WatchKey 加入 pendingKeys 中
if (criticalError) {
implCancelKey(key);
key.signal();
}
}
}
參考:
- 《NIO.2:WatchService、WatchKey(監控檔案變化)》:(https://blog.csdn.net/lirx_tech/article/details/51425364)
每天用心記錄一點點。內容也許不重要,但習慣很重要!