由於業務需要在主備切換時讀取程序返回值文件,這樣一來就涉及到了文件監控讀取的問題,比較low的做法是程序備進程變主進程後每隔一段時間去輪詢相關的文件,並讀取裏面的返回值。後來突然想到了socket相關有個NIO機制,於是覺得socket和文件對操作系統其實都是外設,應該有相關的NIO機制。後來發現java單獨提供了一個很好用的NIO相關機制。
例子比較簡單,直接分析:
WatchService watcher = null;
Pathpath1 = Paths.get("D:\\data2");
Pathpath2 = Paths.get("D:\\data");
try{
watcher= FileSystems.getDefault().newWatchService();
path1.register(watcher,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
path2.register(watcher,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
}catch (IOException e1) {
//TODO Auto-generated catch block
e1.printStackTrace();
}
while(true){
try{
WatchKeykey = watcher.take();
for(WatchEventevent : key.pollEvents()){
System.out.println(key.toString());
WatchEvent.Kindkind = event.kind();
WatchEvente = (WatchEvent)event;
Path fileName = (Path)e.context();
System.out.printf("Event%s has happened,which fileName is %s%n"
,kind.name(),fileName);
key.reset();
}
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
1.首先創建兩個Path對象,其實就是文件節點對象
2.由於windows和linux系統的watcher實現類是不同的,所以需要watcher= FileSystems.getDefault().newWatchService();動態獲取當前文件系統的watcher實現類對象。
3. 對1創建的兩個Path對象進行watcher註冊,監控兩個Path的一舉一動。(注意NIO只能監控到Path對象本身和它的子文件或者子目錄一層)
path1.register(watcher,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
path2.register(watcher,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
--其中ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY是要監控的事件,不聲明的事件將不會觸發通知。
4. 接下來在死循環中調用WatchKeykey = watcher.take();進行阻塞等待事件,當watcher發現有需要監控的事件發生時會喚醒阻塞線程,進行下一步操作。
5. 在子循環中,獲得相關的發生的事件,for(WatchEventevent : key.pollEvents()),並迭代打印相關事件的具體信息。
6. 最後要重新設置key.reset();,使之在下次循環中再次阻塞等待
進一步分析:
根據源碼進一步深入分析,會發現,整個NIO監控機制實際是使用了生產者消費者模式的方式來實現的。
sun.nio.fs.WindowsWatchService.take()
實際是使用了父類的方法:
AbstractWatchService.take()
private finalLinkedBlockingDeque<WatchKey> pendingKeys =
new LinkedBlockingDeque<WatchKey>();
public final WatchKey take()
throws InterruptedException
{
checkOpen();
WatchKey key = pendingKeys.take();
checkKey(key);
return key;
}
--可見是用到了LinkedBlockingDeque這個阻塞隊列容器的take()方法
public E take() throws InterruptedException{
return takeFirst();
}
---------à
private final Condition notEmpty =lock.newCondition();
public E takeFirst() throwsInterruptedException {
finalReentrantLock lock = this.lock;
lock.lock();
try {
E x;
while ( (x = unlinkFirst()) == null)
notEmpty.await();
return x;
} finally {
lock.unlock();
}
}
---看到沒,典型的生產者消費者模式,當隊列容器爲空時,則阻塞等待,當往隊列中填寫元素時就喚醒阻塞線程:
private boolean linkFirst(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)
return false;
Node<E> f = first;
node.next = f;
first = node;
if (last == null)
last = node;
else
f.prev = node;
++count;
notEmpty.signal();
return true;
}
比較好的參考資料:
http://blog.csdn.net/lirx_tech/article/details/51425364