在服务器中,我在第一次访问资源时初始化该资源的工作目录。可能存在对服务器的多个进程处理的资源的并行请求,这意味着我需要注意没有一个进程看到部分初始化的工作目录。解决方案是在临时同级目录中初始化工作目录,然后使用 Files.move
和 StandardCopyOption.ATOMIC_MOVE
将其移动到最终位置。
如果两个进程同时初始化工作目录,则第二个原子移动会失败。这实际上并不是一个问题,因为工作目录已正确初始化,因此第二个进程只需丢弃它创建的临时目录并继续。
我尝试使用以下代码来执行此操作:
private void initalizeWorkDirectory(final Resource resource) throws IOException {
File workDir = resource.getWorkDirectory();
if (!workDir.exists()) {
File tempDir = createTemporarySibligDirectory(workDir);
try {
fillWorkDirectory(tempDir, resource);
Files.move(tempDir.toPath(), workDir.toPath(), StandardCopyOption.ATOMIC_MOVE);
} catch (FileAlreadyExistsException e) {
// do some logging
} finally {
FileUtils.deleteQuietly(tempDir);
}
}
}
但是我注意到,仅捕获 FileAlreadyExistsException
似乎还不够。如果发生移动冲突,还会引发其他异常。我不只是想捕获所有异常,因为这可能隐藏真正的问题。
那么,是否有一种方法可以从 Files.move 抛出的异常中可靠地检测到目录的原子移动因目标目录已存在而失败?
最佳答案
通过观察异常并查看 Windows 和 Linux 的 FileSystemProvider
实现,我们发现了以下内容:
- 在 Windows 上,尝试将目录移动到现有目录时会引发
AccessDeniedException
。 - 在 Unix(及其相关系统)上,会引发通用 FileSystemException 并附带一条对应于
errno.h
constantENOTEMPTY
的消息。 .
根据这些实现细节进行编程时,可以很好地检测目录移动冲突,而不会带来太多隐藏其他问题的危险。
以下代码实现了一个原子移动,在发生移动冲突时始终抛出 FileAlreadyExistsException
:
import java.io.File;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class AtomicMove {
private static final String ENOTEMPTY = "Directory not empty";
public static void move(final File source, final File target) throws FileAlreadyExistsException, IOException {
try {
Files.move(source.toPath(), target.toPath(), StandardCopyOption.ATOMIC_MOVE);
} catch (AccessDeniedException e) {
// directory move collision on Windows
throw new FileAlreadyExistsException(source.toString(), target.toString(), e.getMessage());
} catch (FileSystemException e) {
if (ENOTEMPTY.equals(e.getReason())) {
// directory move collision on Unix
throw new FileAlreadyExistsException(source.toString(), target.toString(), e.getMessage());
} else {
// other problem
throw e;
}
}
}
}
关于java - 如何可靠地检测目录的原子移动因目标已存在而失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30755070/