Java WatchService是NIO.2提供的轻量级文件监听机制,基于系统原生API实现高效响应,但仅支持单目录非递归监控,需手动处理递归注册、事件合并、路径解析及资源释放等细节。

Java 的 WatchService 是 NIO.2 提供的轻量级文件系统事件监听机制,适合监控单个目录(不递归子目录)下的创建、修改、删除等操作。它底层依赖操作系统原生通知(如 Linux inotify、Windows ReadDirectoryChangesW),响应快、资源占用低,但需注意其局限性——比如不支持跨平台递归监听、事件可能合并、需手动处理重复事件等。
基础用法:注册目录并接收事件
WatchService 本身不主动轮询,而是通过阻塞式方法 take() 或非阻塞式 poll() 获取已排队的 WatchKey,每个 key 关联一组待处理事件。
- 先调用
FileSystems.getDefault().newWatchService()创建服务实例 - 用
Path.register(WatchService, WatchEvent.Kind...)注册目标目录,传入StandardWatchEventKinds.ENTRY_CREATE、ENTRY_MODIFY、ENTRY_DELETE - 循环调用
watchService.take()获取 key;对每个 key 调用key.pollEvents()遍历事件 - 处理完事件后必须调用
key.reset(),否则该 key 不再接收新事件
关键细节:事件类型与路径解析
WatchEvent 中的 context() 返回的是**相对路径**(相对于注册目录),不是绝对路径;kind() 表示事件类型,count() 在某些系统中表示事件触发次数(如多次写入可能合并为一个 MODIFY)。
-
创建事件:触发时文件/目录已存在,但内容可能未写完(尤其大文件),建议配合
Files.isRegularFile()和大小判断是否就绪 - 修改事件:文本编辑器常“先删后写”,表现为 DELETE + CREATE;而保存覆盖则多为 MODIFY;二进制工具(如图片编辑)更倾向直接 MODIFY
-
删除事件:被删路径已不可访问,
context()只是名称字符串,无法再用Files.exists()检查
实用增强:支持子目录递归监听
WatchService 默认不递归,需手动遍历子目录并逐个注册。推荐封装一个递归注册方法,并在收到 CREATE 事件时判断是否为目录,若是则立即对其注册监听:
立即学习“Java免费学习笔记(深入)”;
- 初始化时用
Files.walkFileTree()遍历所有子目录,全部注册 - 监听到
ENTRY_CREATE后,用Files.isDirectory(absolutePath)判断是否为目录,是则调用path.register(...) - 注意避免重复注册:可维护一个
Set<path></path>记录已注册路径,或捕获IllegalStateException(已被注册)
健壮性建议:异常处理与资源释放
WatchService 是系统资源,必须显式关闭;监听线程应捕获 ClosedWatchServiceException 和 InterruptedException,避免崩溃导致监听中断。
- 使用
try-with-resources包裹 WatchService(JDK 7+ 支持自动关闭) - 监听循环中,若
take()被中断,应退出线程并关闭 service;若 key 的isValid()返回 false(如目录被删或权限变更),应从监听列表中移除 - 避免在事件处理器中执行耗时操作(如解析大文件),建议将事件转发到线程池异步处理
不复杂但容易忽略:WatchService 是低阶 API,适合定制化场景;若需开箱即用的递归监听、去重、跨平台稳定性,可考虑 Apache Commons IO 的 FileMonitor 或第三方库如 io.methvin.watchservice(支持 macOS/Linux/Windows 且自动递归)。


















