4

我正在使用 Apache Commons IO:

     FileUtils.copyFileToDirectory(srcFile, destDir)

如何让 Windows 在复制期间锁定目标文件?如果我使用,Windows 会正确锁定文件:

      Runtime.getRuntime().exec(
      "cmd /c copy /Y \"" + srcFile.getCanonicalPath() + "\" \""
          + destDir.getCanonicalPath() + "\"").waitFor();

注意:争用不是本地程序,而是外部程序。正在将文件复制到远程系统。远程系统在完成复制之前正在处理文件。因为系统是 Windows,所以普通副本会锁定文件并阻止外部程序访问。

4

3 回答 3

6

java.nio.channels.FileChannel将允许您使用底层文件系统的本地方法获取文件上的FileLock,假设支持此类功能。

此锁跨机器上的进程运行,甚至是非 java 进程。(实际上,锁是代表特定的 JVM 实例持有的,因此不适合管理一个进程中的多个线程之间的争用,或者同一个 JVM 中的多个进程之间的争用)。

这里有很多警告,但如果您在 Windows 上工作,则值得研究。

从javadoc:

此文件锁定 API 旨在直接映射到底层操作系统的本机锁定设施。因此,文件上的锁应该对所有有权访问该文件的程序可见,而不管这些程序是用什么语言编写的。

锁是否实际上阻止另一个程序访问锁定区域的内容是系统相关的,因此未指定。某些系统的本机文件锁定工具只是建议性的,这意味着程序必须协同遵守已知的锁定协议以保证数据完整性。在其他系统上,本地文件锁是强制性的,这意味着如果一个程序锁定了文件的某个区域,那么实际上会阻止其他程序以违反锁的方式访问该区域。在其他系统上,本机文件锁是建议性的还是强制性的,可以根据每个文件进行配置。为确保跨平台的一致和正确行为,强烈建议将此 API 提供的锁用作建议锁。

在某些系统上,获取文件区域的强制锁定会阻止该区域映射到内存中,反之亦然。结合锁定和映射的程序应该为这种结合失败做好准备。

在某些系统上,关闭通道会释放 Java 虚拟机在底层文件上持有的所有锁,无论这些锁是通过该通道还是通过同一文件上打开的另一个通道获得的。强烈建议在程序中使用唯一通道来获取任何给定文件上的所有锁。

某些网络文件系统仅在锁定区域是页面对齐的并且是底层硬件页面大小的整数倍时才允许对内存映射文件使用文件锁定。某些网络文件系统不会在超出某个位置(通常为 230 或 231)的区域上实现文件锁定。通常,在锁定驻留在网络文件系统上的文件时应格外小心。

于 2009-02-02T16:05:16.450 回答
4

Java 本身不支持文件锁定。

如果文件的争用来自您的程序内部,那么您可能需要在文件副本之上构建额外的同步,以确保并发写入不会相互干扰。但是,如果争用来自软件外部的某个地方,那么您无能为力。您可以尝试将文件写入临时目录,然后重命名它,因为重命名或多或少是原子的(取决于文件系统)。

这将有助于获得有关为什么首先需要锁定文件的更多信息。

争论不在于本地程序,而在于外部程序。正在将文件复制到远程系统。远程系统在完成复制之前正在处理文件。因为系统是 Windows,所以普通副本会锁定文件并阻止外部程序访问。

在这种情况下,您应该尝试写入临时文件,然后在文件完全复制后重命名。文件重命名是原子操作(在非网络文件系统上),因此它应该适合您。

于 2009-01-23T16:17:40.843 回答
1

复制文件时,请始终复制到临时文件名,并在完成写入后重命名文件。这样,另一个进程永远不会看到损坏的文件。

更好的是,您可以写入 *.tmp。然后检查目标文件是否存在。将 dest 文件重命名为 *.bak(如果存在)。最后重命名 *.tmp -> dest。

于 2009-01-23T16:26:24.710 回答