Java NIO之文件監控機制

由於業務需要在主備切換時讀取程序返回值文件,這樣一來就涉及到了文件監控讀取的問題,比較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


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