java的watchservice是jdk 7引入的nio.2特性,基于操作系统原生机制(如inotify)高效监听文件事件;需先获取服务实例并为单目录注册事件类型,子目录须递归注册;通过take()阻塞获取watchkey,再pollevents()处理事件,处理后必须reset();注意不递归、事件可能合并、路径为相对路径及目录删除导致key失效等跨平台陷阱。

Java 的 WatchService 是 JDK 7 引入的 NIO.2 特性,用于高效、轻量地监听文件系统事件。它底层基于操作系统原生通知机制(如 Linux 的 inotify、Windows 的 ReadDirectoryChangesW),避免轮询开销。
注册监控路径并监听事件类型
WatchService 本身不直接监听,需先获取服务实例,再为指定目录注册感兴趣的事件类型。注意:只支持对单个目录注册,子目录需递归注册(或使用第三方库如 Apache Commons IO 封装)。
- 调用
FileSystems.getDefault().newWatchService()获取服务 - 用
Path.register()方法注册路径,传入 WatchService 和事件种类(ENTRY_CREATE、ENTRY_MODIFY、ENTRY_DELETE) - 可同时注册多个事件,用逗号分隔:
path.register(watcher, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE)
阻塞式获取并处理事件
WatchService 的事件是异步产生的,但获取方式是阻塞式的 —— 调用 watcher.take() 会一直等待直到有事件到达;若想非阻塞,可用 poll() 或带超时的 poll(long, TimeUnit)。
- 每次
take()返回一个WatchKey,代表一批发生在同一目录下的事件 - 通过
key.pollEvents()获取该批所有事件列表,每个事件含类型、文件名(相对路径)、是否为重命名等上下文 - 处理完必须调用
key.reset(),否则该 key 不再接收新事件;若返回false表示通道已关闭或目录被删除
注意跨平台行为与常见陷阱
WatchService 行为受操作系统限制,实际使用中需留意以下细节:
-
不递归:注册
/a只监听其下一级文件/目录变动,/a/b/c.txt修改不会触发 —— 需手动遍历子目录逐个注册 -
事件可能合并:连续多次修改可能只报告一次
ENTRY_MODIFY;新建+写入可能只看到ENTRY_CREATE -
文件名是相对路径:事件中的
context()返回的是触发事件的条目名(如"test.txt"),不是绝对路径,需结合注册的原始 Path 拼接 -
删除监听目录会导致 key 失效:若被监听目录被删,对应 WatchKey 会失效,
reset()返回false,应清理资源
简易可用示例结构
典型用法是一个独立线程持续 take() 并分发事件。以下为关键骨架(省略异常处理和资源关闭):
WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Paths.get("/tmp/watch");
dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
while (true) {
WatchKey key = watcher.take(); // 阻塞等待
for (WatchEvent> event : key.pollEvents()) {
WatchEvent.Kind> kind = event.kind();
Path filename = (Path) event.context();
System.out.println(kind + " on " + dir.resolve(filename));
}
if (!key.reset()) break; // 目录不可达时退出
}
实际项目中建议配合 ExecutorService 异步处理事件,避免阻塞监听线程;同时做好异常捕获与重连逻辑,应对权限变化或路径消失等情况。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











