不知道大家监听目录下的文件变化时是否还是在使用轮询的方式去记录、比对文件状态
今天介绍一个接口 WatchService
简单好用
监视注册对象的更改和事件的监视服务。例如,文件管理器可以使用监视服务来监视目录的更改,以便它可以在创建或删除文件时更新其文件列表的显示。
对象Watchable
通过调用其方法向监视服务注册register
,返回 WatchKey
表示注册。当检测到对象的事件时,会向键发出信号,如果当前未发出信号,则会将其排队到监视服务,以便调用 poll
或take
方法来检索键并处理事件的消费者可以检索它。一旦事件被处理,消费者调用密钥的reset
方法来重置密钥,这允许Key被发信号并与进一步的事件重新排队。
通过调用 key 的方法可以取消手表服务的注册 cancel
。取消时排队的密钥将保留在队列中,直到被检索。根据对象的不同,可能会自动取消某个键。例如,假设监视一个目录,并且监视服务检测到该目录已被删除或其文件系统不再可访问。当以这种方式取消某个键时,如果当前未发出信号,则会发出信号并排队。为了确保通知消费者,该reset
方法的返回值指示密钥是否有效。
监视服务可供多个并发消费者安全使用。为了确保任何时候只有一个使用者处理特定对象的事件,则应注意确保reset
仅在处理其事件后才调用该键的方法。close
可以随时调用该方法来关闭服务,从而导致任何等待检索密钥的线程抛出ClosedWatchServiceException
。
文件系统报告事件的速度可能比检索或处理事件的速度快,并且实现可能对其可能累积的事件数量施加未指定的限制。如果实现故意 丢弃事件,那么它会安排键的pollEvents
方法返回事件类型为 的元素OVERFLOW
。消费者可以使用该事件作为触发器来重新检查对象的状态。
当报告事件表明监视目录中的文件已被修改时,不能保证修改该文件的程序已完成。应注意协调与可能更新该文件的其他程序的访问。该类FileChannel
定义了锁定文件区域以防止其他程序访问的方法。
import java.nio.file.*;
import java.util.concurrent.TimeUnit;
/**
* @author brucewee
* @date 2024/1/2 22:25
*/
public class WatchServiceTest {
public static void main(String[] args) {
try {
//声明一个watchservice
WatchService watchService = FileSystems.getDefault().newWatchService();
//在要监听的目录上注册watchservice 及要监听的事件
String dir = "D:\\test";
Path path = Paths.get(dir);
if(Files.notExists(path)){
Files.createDirectory(path);
}
path.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.OVERFLOW);
while(true){
//WatchKey key = watchService.take(); //使用take会一直阻塞 直接该目录下有文件变化
//poll 可以在指定时间内拿不到就返回
WatchKey key = watchService.poll(3, TimeUnit.SECONDS);
if(key != null){
//获取监听到的事件
for(WatchEvent<?> event : key.pollEvents()){
//每个事件进行处理
if(event.kind() == StandardWatchEventKinds.ENTRY_CREATE){
System.out.println("add " + dir + event.context());
}
if(event.kind() == StandardWatchEventKinds.ENTRY_DELETE){
System.out.println("delete " + dir + event.context());
}
if(event.kind() == StandardWatchEventKinds.ENTRY_MODIFY){
System.out.println("modify " + dir + event.context());
}
//这种情况有可能监控的文件太多导致溢出
if(event.kind() == StandardWatchEventKinds.OVERFLOW){
//do something
}
}
//每次监听结束后要重置key
key.reset();
}
//break condition
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
add D:\test新建 文本文档.txt
modify D:\test新建 文本文档.txt
delete D:\test新建 文本文档.txt