Java WatchService 在 Windows 上锁定了目录
Java WatchService locks directory on Windows
似乎当您使用 Java 的 WatchService 监视目录然后尝试重命名其父目录时,重命名将失败并出现 AccessDeniedException。该目录似乎被 WatchService 锁定了。
可以通过以下方式复制:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.*;
public class WatcherTest {
@Test
public void moveWatchedDir(@TempDir Path tempDir) throws Exception {
Files.createDirectories(tempDir.resolve("dir1/dir2"));
var watchService = FileSystems.getDefault().newWatchService();
tempDir.resolve("dir1/dir2").register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
Files.move(tempDir.resolve("dir1"), tempDir.resolve("dir1_b"));
}
}
失败:
java.nio.file.AccessDeniedException: C:\Users\Markus\AppData\Local\Temp\junit14649009910061913524\dir1 -> C:\Users\Markus\AppData\Local\Temp\junit14649009910061913524\dir1_b
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:89)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsFileCopy.move(WindowsFileCopy.java:395)
at java.base/sun.nio.fs.WindowsFileSystemProvider.move(WindowsFileSystemProvider.java:292)
at java.base/java.nio.file.Files.move(Files.java:1426)
at WatcherTest.moveWatchedDir(WatcherTest.java:13)
已在 Windows 10 上使用 OpenJDK 11 和 14 进行测试。尝试在 Windows 资源管理器中重命名 dir1 也失败。
在 Linux.
上按预期工作
另请注意,当观察者未在 dir1/dir2 上注册但仅在 dir1 上时,它会起作用。
这是 OpenJDK
中的错误吗?查看一些较旧的问题 (https://bugs.openjdk.java.net/browse/JDK-8153925),似乎不应发生目录锁定。
不,这不是错误。这是 Windows 的一个设计特性,称为 强制锁定 。无法禁用它。
Linux 使用 建议锁定 这意味着它不会阻止重命名甚至删除目录。
@jurez 回答正确,在 Windows 上 WatchService 将锁定目录。这是一个 known OpenJDK issue,显然无法修复。
但是有一个很好的解决方法。在 Windows 上可以监视整个目录结构,而不必像在 Linux.
上那样为每个子目录手动注册观察者
这应该可以解决很多情况下的问题。使用此解决方法,我只需要 watch/lock 顶级目录,这不会给用户带来问题。
似乎当您使用 Java 的 WatchService 监视目录然后尝试重命名其父目录时,重命名将失败并出现 AccessDeniedException。该目录似乎被 WatchService 锁定了。
可以通过以下方式复制:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.*;
public class WatcherTest {
@Test
public void moveWatchedDir(@TempDir Path tempDir) throws Exception {
Files.createDirectories(tempDir.resolve("dir1/dir2"));
var watchService = FileSystems.getDefault().newWatchService();
tempDir.resolve("dir1/dir2").register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
Files.move(tempDir.resolve("dir1"), tempDir.resolve("dir1_b"));
}
}
失败:
java.nio.file.AccessDeniedException: C:\Users\Markus\AppData\Local\Temp\junit14649009910061913524\dir1 -> C:\Users\Markus\AppData\Local\Temp\junit14649009910061913524\dir1_b
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:89)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsFileCopy.move(WindowsFileCopy.java:395)
at java.base/sun.nio.fs.WindowsFileSystemProvider.move(WindowsFileSystemProvider.java:292)
at java.base/java.nio.file.Files.move(Files.java:1426)
at WatcherTest.moveWatchedDir(WatcherTest.java:13)
已在 Windows 10 上使用 OpenJDK 11 和 14 进行测试。尝试在 Windows 资源管理器中重命名 dir1 也失败。 在 Linux.
上按预期工作另请注意,当观察者未在 dir1/dir2 上注册但仅在 dir1 上时,它会起作用。
这是 OpenJDK
中的错误吗?查看一些较旧的问题 (https://bugs.openjdk.java.net/browse/JDK-8153925),似乎不应发生目录锁定。
不,这不是错误。这是 Windows 的一个设计特性,称为 强制锁定 。无法禁用它。
Linux 使用 建议锁定 这意味着它不会阻止重命名甚至删除目录。
@jurez 回答正确,在 Windows 上 WatchService 将锁定目录。这是一个 known OpenJDK issue,显然无法修复。
但是有一个很好的解决方法。在 Windows 上可以监视整个目录结构,而不必像在 Linux.
上那样为每个子目录手动注册观察者这应该可以解决很多情况下的问题。使用此解决方法,我只需要 watch/lock 顶级目录,这不会给用户带来问题。