NIO 之 WatchService

NIO 之 WatchService

Java 1.6版本以前是不存在目錄監控的API的。如果要實現這種功能必須要自己遍歷目錄,記錄各個文件的情況,然後定時全部遍歷一次,從 JDK7 之後出現了 WatchService 類,實現了對目錄下文件的監控。

整體流程

整個監控目錄文件操作的流程大致如下:

  1. 獲取 WatchService
  2. 註冊指定目錄的監視器 WatchService
  3. 等待目錄下的文件發生變化
  4. 對發生變化的文件進行操作

獲取 WatchService 實例

WatchService 類的實現實際上是對操作系統的文件監視器的封裝,相比之前的手動實現,優雅了不少。因爲不需要遍歷文件整體而言效率也高很多。以下爲獲取 WatchService 實例的代碼,通過 FileSystem.getDefault() 可看出並非是自己實現的。從 newWatchService() 方法名看, WatchService 可以獲取多個。

WatchService watchService = FileSystems.getDefault().newWatchService();

實際上調用此方法後,程序會新開一個線程,監視文件變化發出的信號,此時線程尚未就緒。

爲目錄註冊監視器

有了監視器,接下來我們需要註冊監視器了。

Path path = Paths.get("src");
WatchKey watchKey = path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);

註冊監視器需要用到 Path 實例,該實例對應的必須是一個目錄,不允許是一個文件。
方法比較簡單,就是說爲目錄註冊一個監視器,監視目錄下文件的變化。
關於 StandardWatchEventKinds.ENTRY_MODIFY ,表示監視文件的修改事件,它是 WatchEvent.Kind<?> 的實現類。
看起來它像是枚舉,實際上它並不是。JDK 中幫我們定義了三種事件,新增、修改和刪除。

獲取目錄下的變化

獲取目錄的變化需要使用 WatchServicetake() 方法或 poll() 方法。

WatchKey key = watchService.take();
WatchKey pollKey = watchService.poll();

take() 是一個阻塞方法,會等待監視器發出的信號才返回。
poll() 是一個非阻塞方法,會立即返回當時監視器中是否有信號。
返回的 WatchKey 對象,實際上是一個單例,和之前 path.register() 方法返回的實例是同一個。它只能保存某一時間點的文件變化信息。

處理文件變化事件

List<WatchEvent<?>> events = key.pollEvents();
for (WatchEvent<?> pollEvent : events) {
    Object o = pollEvent.context();
    WatchEvent.Kind kind = pollEvent.kind();
}

key.reset();

pollEvents() 用於獲取文件變化事件,只能獲取一次,不能重複獲取,類似隊列的形式。
context() 返回觸發該事件的那個文件或目錄的路徑(相對路徑)
kind() 返回事件類型(ENTRY_CREATE、ENTRY_DELETE、ENTRY_MODIFY之一)
reset() 每次調用 WatchService 的 take() 或 poll() 方法時需要通過本方法重置。

一個簡單的例子

public void watchServiceExample() throws IOException, InterruptedException {
    WatchService watchService = FileSystems.getDefault().newWatchService();

    Path path = Paths.get("D:/code");

    WatchKey watchKey = path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);

    while (true) {
        // 嘗試獲取下一個變化信息的監控池,如果沒有變化則一直等待
        WatchKey key = watchService.take();

        for (WatchEvent<?> pollEvent : key.pollEvents()) {
            System.out.println(String.format("%s is %s.", pollEvent.context().toString(), pollEvent.kind().name().substring(6)));
        }

        if (!key.reset()) {
            break;
        }
    }
}

總結

WatchService 的優點就不用多說了,這裏就說一個缺點: 只能監視當前目錄下的文件和目錄,不能監視子目錄


參考

[瘋狂Java]NIO.2:WatchService、WatchKey(監控文件變化)

本文如有問題,歡迎在評論區中指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章