java.nio.file.WatchService?實(shí)時監(jiān)控文件變化的示例代碼
在平時的開發(fā)過程中,會有很多場景需要實(shí)時監(jiān)聽文件的變化,如下:
1、通過實(shí)時監(jiān)控 mysql 的 binlog 日志實(shí)現(xiàn)數(shù)據(jù)同步
2、修改配置文件后,希望系統(tǒng)可以實(shí)時感知
3、應(yīng)用系統(tǒng)將日志寫入文件中,日志監(jiān)控系統(tǒng)可以實(shí)時抓取日志,分析日志內(nèi)容并進(jìn)行報警
4、類似 ide 工具,可以實(shí)時感知管理的工程下的文件變更
在 Java 語言中,從 JDK7 開始,新增了java.nio.file.WatchService
類,用來實(shí)時監(jiān)控文件的變化。
1.示例代碼
FileWatchedService 類:
package org.learn.file; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.List; /** * 實(shí)時監(jiān)控文件的變化 * * @author zhibo * @date 2019-07-30 20:37 */ public class FileWatchedService { private WatchService watchService; private FileWatchedListener listener; /** * * @param path 要監(jiān)聽的目錄,注意該 Path 只能是目錄,否則會報錯 java.nio.file.NotDirectoryException: /Users/zhibo/logs/a.log * @param listener 自定義的 listener,用來處理監(jiān)聽到的創(chuàng)建、修改、刪除事件 * @throws IOException */ public FileWatchedService(Path path, FileWatchedListener listener) throws IOException { watchService = FileSystems.getDefault().newWatchService(); path.register(watchService, /// 監(jiān)聽文件創(chuàng)建事件 StandardWatchEventKinds.ENTRY_CREATE, /// 監(jiān)聽文件刪除事件 StandardWatchEventKinds.ENTRY_DELETE, /// 監(jiān)聽文件修改事件 StandardWatchEventKinds.ENTRY_MODIFY); // // path.register(watchService, // new WatchEvent.Kind[]{ // StandardWatchEventKinds.ENTRY_MODIFY, // StandardWatchEventKinds.ENTRY_CREATE, // StandardWatchEventKinds.ENTRY_DELETE // }, // SensitivityWatchEventModifier.HIGH); this.listener = listener; } private void watch() throws InterruptedException { while (true) { WatchKey watchKey = watchService.take(); List<WatchEvent<?>> watchEventList = watchKey.pollEvents(); for (WatchEvent<?> watchEvent : watchEventList) { WatchEvent.Kind kind = watchEvent.kind(); WatchEvent<Path> curEvent = (WatchEvent<Path>) watchEvent; if (kind == StandardWatchEventKinds.OVERFLOW) { listener.onOverflowed(curEvent); continue; } else if (kind == StandardWatchEventKinds.ENTRY_CREATE) { listener.onCreated(curEvent); continue; } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) { listener.onModified(curEvent); continue; } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) { listener.onDeleted(curEvent); continue; } } /** * WatchKey 有兩個狀態(tài): * {@link sun.nio.fs.AbstractWatchKey.State.READY ready} 就緒狀態(tài):表示可以監(jiān)聽事件 * {@link sun.nio.fs.AbstractWatchKey.State.SIGNALLED signalled} 有信息狀態(tài):表示已經(jīng)監(jiān)聽到事件,不可以接續(xù)監(jiān)聽事件 * 每次處理完事件后,必須調(diào)用 reset 方法重置 watchKey 的狀態(tài)為 ready,否則 watchKey 無法繼續(xù)監(jiān)聽事件 */ if (!watchKey.reset()) { break; } } } public static void main(String[] args) { try { Path path = Paths.get("/Users/zhibo/logs/"); FileWatchedService fileWatchedService = new FileWatchedService(path, new FileWatchedAdapter()); fileWatchedService.watch(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }
FileWatchedListener 類:
package org.learn.file; import java.nio.file.Path; import java.nio.file.WatchEvent; public interface FileWatchedListener { void onCreated(WatchEvent<Path> watchEvent); void onDeleted(WatchEvent<Path> watchEvent); void onModified(WatchEvent<Path> watchEvent); void onOverflowed(WatchEvent<Path> watchEvent); }
FileWatchedAdapter 類:
package org.learn.file; import java.nio.file.Path; import java.nio.file.WatchEvent; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; /** * 文件監(jiān)聽適配器 * * @author zhibo * @date 2019-07-31 11:07 */ public class FileWatchedAdapter implements FileWatchedListener { @Override public void onCreated(WatchEvent<Path> watchEvent) { Path fileName = watchEvent.context(); System.out.println(String.format("文件【%s】被創(chuàng)建,時間:%s", fileName, now())); } @Override public void onDeleted(WatchEvent<Path> watchEvent) { Path fileName = watchEvent.context(); System.out.println(String.format("文件【%s】被刪除,時間:%s", fileName, now())); } @Override public void onModified(WatchEvent<Path> watchEvent) { Path fileName = watchEvent.context(); System.out.println(String.format("文件【%s】被修改,時間:%s", fileName, now())); } @Override public void onOverflowed(WatchEvent<Path> watchEvent) { Path fileName = watchEvent.context(); System.out.println(String.format("文件【%s】被丟棄,時間:%s", fileName, now())); } private String now(){ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); return dateFormat.format(Calendar.getInstance().getTime()); } }
執(zhí)行以上代碼,啟動監(jiān)控任務(wù),然后我在/Users/zhibo/logs/
目錄中創(chuàng)建、修改、刪除文件,命令如下:
應(yīng)用程序感知到文件變化,打印日志如下:
2.其實(shí)并沒有實(shí)時
大家可以看到,監(jiān)控任務(wù)基本上是以 10 秒為單位進(jìn)行日志打印的,也就是說修改一個文件,WatchService 10秒之后才能感知到文件的變化,沒有想象中的那么實(shí)時。根據(jù)以上的經(jīng)驗(yàn),推測可能是 WatchService 做了定時的操作,時間間隔為 10 秒。通過翻閱源代碼發(fā)現(xiàn),在 PollingWatchService
中確實(shí)存在一個固定時間間隔的調(diào)度器,如下圖:
該調(diào)度器的時間間隔有 SensitivityWatchEventModifier
進(jìn)行控制,該類提供了 3 個級別的時間間隔,分別為2秒、10秒、30秒,默認(rèn)值為 10秒。SensitivityWatchEventModifier
源碼如下:
package com.sun.nio.file; import java.nio.file.WatchEvent.Modifier; public enum SensitivityWatchEventModifier implements Modifier { HIGH(2), MEDIUM(10), LOW(30); private final int sensitivity; public int sensitivityValueInSeconds() { return this.sensitivity; } private SensitivityWatchEventModifier(int var3) { this.sensitivity = var3; } }
通過改變時間間隔來進(jìn)行驗(yàn)證,將
path.register(watchService, /// 監(jiān)聽文件創(chuàng)建事件 StandardWatchEventKinds.ENTRY_CREATE, /// 監(jiān)聽文件刪除事件 StandardWatchEventKinds.ENTRY_DELETE, /// 監(jiān)聽文件修改事件 StandardWatchEventKinds.ENTRY_MODIFY);
修改為:
path.register(watchService, new WatchEvent.Kind[]{ StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE }, SensitivityWatchEventModifier.HIGH);
查看日志,發(fā)現(xiàn)正如我們的推斷,WatchService 正以每 2 秒的時間間隔感知文件變化。
在 stackoverflow 中也有人提出了該問題,問題:Is Java 7 WatchService Slow for Anyone Else,我的 mac 系統(tǒng)中確實(shí)存在該問題,由于手頭沒有 windows、linux 系統(tǒng),因此無法進(jìn)行這兩個系統(tǒng)的驗(yàn)證。
到此這篇關(guān)于java.nio.file.WatchService 實(shí)時監(jiān)控文件變化的文章就介紹到這了,更多相關(guān)java.nio.file.WatchService 監(jiān)控文件變化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解基于java的Socket聊天程序——服務(wù)端(附demo)
這篇文章主要介紹了詳解基于java的Socket聊天程序——服務(wù)端(附demo),具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-12-12Java基于循環(huán)遞歸回溯實(shí)現(xiàn)八皇后問題算法示例
這篇文章主要介紹了Java基于循環(huán)遞歸回溯實(shí)現(xiàn)八皇后問題算法,結(jié)合具體實(shí)例形式分析了java的遍歷、遞歸、回溯等算法實(shí)現(xiàn)八皇后問題的具體步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-06-06Jenkins環(huán)境搭建實(shí)現(xiàn)過程圖解
這篇文章主要介紹了Jenkins環(huán)境搭建實(shí)現(xiàn)過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09如何使用Spring RestTemplate訪問restful服務(wù)
這篇文章主要介紹了如何使用Spring RestTemplate訪問restful服務(wù),詳細(xì)的介紹了什么是RestTemplate以及簡單實(shí)現(xiàn),非常具有實(shí)用價值,需要的朋友可以參考下2018-10-10Java并發(fā)編程深入理解之Synchronized的使用及底層原理詳解 上
在并發(fā)編程中存在線程安全問題,主要原因有:1.存在共享數(shù)據(jù) 2.多線程共同操作共享數(shù)據(jù)。關(guān)鍵字synchronized可以保證在同一時刻,只有一個線程可以執(zhí)行某個方法或某個代碼塊,同時synchronized可以保證一個線程的變化可見(可見性),即可以代替volatile2021-09-09logback如何去掉DubboMonitor煩人的INFO日志
這篇文章主要介紹了logback如何去掉DubboMonitor煩人的INFO日志方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07springboot自動配置沒有生效的問題定位(條件斷點(diǎn))
這篇文章主要介紹了springboot自動配置未生效問題定位,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,下面我們來學(xué)習(xí)一下吧2019-06-06