Java NIO之文件监控机制

来源:互联网 发布:java游戏引擎 编辑:程序博客网 时间:2024/06/06 02:25

由于业务需要在主备切换时读取程序返回值文件,这样一来就涉及到了文件监控读取的问题,比较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


原创粉丝点击