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