gpt4 book ai didi

java - WatchKey 始终为空

转载 作者:行者123 更新时间:2023-12-03 02:44:51 25 4
gpt4 key购买 nike

我尝试观察某些文件的更改。但我从 watch_object.watch_service.poll(16, TimeUnit.MILLISECONDS); 获得的 WatchKey 始终为 null。控制台没有打印任何错误,所以我有点迷失。

public class FileWatcher implements Runnable {

public FileWatcher() {
}

static public class Watch_Object {
public File file;
public WatchService watch_service;
}

static public HashMap<Object, Watch_Object> watched_files = new HashMap<>();

static public boolean is_running = false;


static public synchronized void watch(Object obj, String filename) {

File file = new File(filename);

if (file.exists()) {

try {

WatchService watcher = null;
watcher = FileSystems.getDefault().newWatchService();

Watch_Object watch_object = new Watch_Object();
watch_object.file = file;
watch_object.watch_service = watcher;

watched_files.put(obj, watch_object);

Path path = file.toPath().getParent();
path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);

if (!is_running) {
(new Thread(new FileWatcher())).start();
is_running = true;
}


}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}

}
else {
// Error
}

}



@Override
public void run() {

try {
while (true) {
synchronized(this) {

for (Watch_Object watch_object : watched_files.values()) {

WatchKey key = watch_object.watch_service.poll(16, TimeUnit.MILLISECONDS);

System.out.println("A");

if (key != null) {

System.out.println("B");

}

}

}
Thread.sleep(16);
}
}
catch (Throwable e) {
// Log or rethrow the error
e.printStackTrace();
}
}

}

运行它:

public static void main(String[] args) {
// the obj is WIP, just use null for now
watch(null, "/Users/doekewartena/Desktop/test_image.png");
}

最佳答案

我想在这个答案前面加上WatchService高度依赖于实现:

Platform dependencies

The implementation that observes events from the file system is intended to map directly on to the native file event notification facility where available, or to use a primitive mechanism, such as polling, when a native facility is not available. Consequently, many of the details on how events are detected, their timeliness, and whether their ordering is preserved are highly implementation specific. For example, when a file in a watched directory is modified then it may result in a single ENTRY_MODIFY event in some implementations but several events in other implementations. Short-lived files (meaning files that are deleted very quickly after they are created) may not be detected by primitive implementations that periodically poll the file system to detect changes.

If a watched file is not located on a local storage device then it is implementation specific if changes to the file can be detected. In particular, it is not required that changes to files carried out on remote systems be detected.

<小时/>

您提到 WatchService.poll 始终返回 null。这并不完全令人惊讶,因为 poll()poll(long,TimeUnit)如果没有要处理的事件,将返回 null - 标准的类似队列的行为。但你说你总是得到null,即使你修改了监视的文件*。不幸的是,我无法使用 OpenJDK 11.0.2(或 JDK 1.8.0_202)、Windows 10 和本地存储设备重现该问题。

*这是在问题评论被清理之前说的。

在尝试您的代码时,我观察到 B 被打印到控制台。诚然,要看到它并不容易,因为每 16 毫秒打印 A 令人难以承受,但它确实存在。但有一个问题,在第一次修改事件之后,它不会再报告了。这让我对您的代码发表一些评论。

  1. 你不打电话WatchKey.reset() .

处理完 WatchKey 后调用此方法非常重要。该方法将 WatchKey 标记为准备检测新事件。如果没有这个调用,您将无法观察到后续事件。

  • 你不 poll the events WatchKey 的。
  • 为了解决看不到后续事件的问题,我天真地添加了对 reset() 的调用,而没有执行任何其他操作。这导致大量的 B 被打印到控制台。我很困惑,因为我只修改了一次文件,但后来我阅读了 WatchKey.reset 的文档(强调我的):

    Resets this watch key.

    If this watch key has been cancelled or this watch key is already in the ready state then invoking this method has no effect. Otherwise if there are pending events for the object then this watch key is immediately re-queued to the watch service. If there are no pending events then the watch key is put into the ready state and will remain in that state until an event is detected or the watch key is cancelled.

    我所看到的只是一遍又一遍地发生相同的事件,因为我从未处理过它。添加对 WatchEvent.pollEvents() 的调用后我不再收到 B 的垃圾邮件。

  • 您为每个要观看的文件创建一个新的 WatchService
  • 您似乎想要一个可以监视任意数量的文件(并且仅监视这些文件)的类。这不需要每个文件都有一个 WatchService,因为您可以使用同一个 WatchService 注册多个目录。如果文件来自不同的FileSystem,则您需要使用多个WatchService。但是,您的代码始终使用 default file system .

    使用相同的 WatchService 也无需使用 poll。我假设您当前使用poll 的原因是因为您需要检查每个WatchService。由于现在只有一个,您可以使用阻止 WatchService.take()方法代替。

    <小时/>

    这是一个小例子,我相信它可以满足您的需求。我不能保证它是完美的,因为它还没有经过彻底的测试。我也不能保证它可以在您的计算机上运行。

    import java.io.Closeable;
    import java.io.IOException;
    import java.nio.file.ClosedWatchServiceException;
    import java.nio.file.FileSystem;
    import java.nio.file.Path;
    import java.nio.file.WatchEvent;
    import java.nio.file.WatchKey;
    import java.nio.file.WatchService;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.Set;
    import java.util.concurrent.CopyOnWriteArrayList;
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.function.BiConsumer;

    /**
    * Watches files for modification events, but not for creation,
    * deletion, or overflow events.
    */
    public class FileWatcher implements Closeable, Runnable {

    private final List<BiConsumer<? super FileWatcher, ? super Path>> handlers
    = new CopyOnWriteArrayList<>();

    private final Object lock = new Object();
    private final Map<Path, Registry> registeredDirs = new HashMap<>();
    private final Set<Path> watchedFiles = new HashSet<>();

    private final AtomicBoolean running = new AtomicBoolean();
    private final FileSystem fileSystem;
    private final WatchService service;

    public FileWatcher(FileSystem fs) throws IOException {
    service = fs.newWatchService();
    fileSystem = fs;
    }

    public FileSystem getFileSystem() {
    return fileSystem;
    }

    public boolean startWatching(Path file) throws IOException {
    Objects.requireNonNull(file);
    synchronized (lock) {
    if (watchedFiles.add(file)) {
    Path directory = file.getParent();
    if (registeredDirs.containsKey(directory)) {
    registeredDirs.get(directory).incrementCount();
    } else {
    try {
    WatchKey key = directory.register(service, ENTRY_MODIFY);
    registeredDirs.put(directory, new Registry(key));
    } catch (ClosedWatchServiceException | IllegalArgumentException
    | IOException | SecurityException ex) {
    watchedFiles.remove(file);
    throw ex;
    }
    }
    return true;
    }
    return false;
    }
    }

    public boolean stopWatching(Path file) {
    Objects.requireNonNull(file);
    synchronized (lock) {
    if (watchedFiles.remove(file)) {
    Path directory = file.getParent();
    Registry registry = registeredDirs.get(directory);
    if (registry.decrementCount()) {
    registeredDirs.remove(directory);
    registry.cancelKey();
    }
    return true;
    }
    return false;
    }
    }

    public void addHandler(BiConsumer<? super FileWatcher, ? super Path> handler) {
    handlers.add(Objects.requireNonNull(handler));
    }

    public void removeHandler(BiConsumer<? super FileWatcher, ? super Path> handler) {
    handlers.remove(Objects.requireNonNull(handler));
    }

    private void fireModifyEvent(Path source) {
    for (BiConsumer<? super FileWatcher, ? super Path> handler : handlers) {
    try {
    handler.accept(this, source);
    } catch (RuntimeException ex) {
    Thread.currentThread().getUncaughtExceptionHandler()
    .uncaughtException(Thread.currentThread(), ex);
    }
    }
    }

    @Override
    public void close() throws IOException {
    service.close();
    synchronized (lock) {
    registeredDirs.clear();
    watchedFiles.clear();
    }
    }

    @Override
    public void run() {
    if (running.compareAndSet(false, true)) {
    try {
    while (!Thread.interrupted()) {
    WatchKey key = service.take();
    for (WatchEvent<?> event : key.pollEvents()) {
    Path source = ((Path) key.watchable())
    .resolve((Path) event.context());
    boolean isWatched;
    synchronized (lock) {
    isWatched = watchedFiles.contains(source);
    }
    if (isWatched) {
    fireModifyEvent(source);
    }
    }
    key.reset();
    }
    } catch (InterruptedException ignore) {
    } finally {
    running.set(false);
    }
    } else {
    throw new IllegalStateException("already running");
    }
    }

    private static class Registry {

    private final WatchKey key;
    private int count;

    private Registry(WatchKey key) {
    this.key = key;
    incrementCount();
    }

    private void incrementCount() {
    count++;
    }

    private boolean decrementCount() {
    return --count <= 0;
    }

    private void cancelKey() {
    key.cancel();
    }

    }

    }

    还有一个使用上述FileWatcher的小应用程序:

    import java.io.IOException;
    import java.nio.file.FileSystems;
    import java.nio.file.Files;
    import java.nio.file.LinkOption;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.Scanner;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;

    public class Main {

    public static void main(String[] args) throws IOException {
    Path file = chooseFile();
    if (file == null) {
    return;
    }
    System.out.println("Entered \"" + file + "\"");

    ExecutorService executor = Executors.newSingleThreadExecutor();
    try (FileWatcher watcher = new FileWatcher(FileSystems.getDefault())) {
    Future<?> task = executor.submit(watcher);
    executor.shutdown();

    watcher.addHandler((fw, path) -> System.out.println("File modified: " + path));

    watcher.startWatching(file);

    waitForExit();
    task.cancel(true);
    } finally {
    executor.shutdownNow();
    }
    }

    private static Path chooseFile() {
    Scanner scanner = new Scanner(System.in);
    while (true) {
    System.out.print("Enter file (or 'exit' to exit application): ");
    String line = scanner.nextLine();
    if ("exit".equalsIgnoreCase(line.trim())) {
    return null;
    }
    Path file = Paths.get(line).toAbsolutePath().normalize();
    if (Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS)) {
    return file;
    }
    System.out.println("File must exist and be a regular file. Try again.");
    }
    }

    private static void waitForExit() {
    System.out.println("\nType 'exit' to exit the application.");
    Scanner scanner = new Scanner(System.in);
    while (true) {
    String line = scanner.nextLine();
    if ("exit".equalsIgnoreCase(line.trim())) {
    return;
    }
    }
    }

    }

    以及它的实际操作的 GIF:

    enter image description here

    关于java - WatchKey 始终为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54749397/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com