多线程访问 Java 中的文件

Multithreaded access to files in Java

我正在 Java 的多线程服务器上工作。 服务器监视文件目录。客户端可以询问服务器:

为了进行传输,我计划使用 FileChannels 和 SocketChannels,使用方法 transferFrom 和 transferTo。根据文档,这两种方法是线程安全的。 问题是对这两个函数的一次调用不足以 read/write 整个文件。

如果同时对同一个文件有多个请求,就会出现问题。在这种情况下,多个线程可能对同一个文件执行 read/write 操作。现在,根据 Java 文档,对 transferFrom/transferTo 的单个调用是线程安全的。但是对这两个函数的一次调用不足以 read/write 整个文件。如果线程 A 正在回复下载请求,而线程 B 正在回复引用同一文件的上传请求,则可能会发生:

  1. 线程 A 开始读取文件
  2. 在线程 A 中,出于某种原因,在 EOF
  3. 之前读取调用 returns
  4. 线程 B 通过一次写入调用覆盖整个文件
  5. 线程 A 继续读取文件

此时下载客户端收到一部分旧版本和一部分新版本


为了解决这个问题,我想我应该使用某种锁定,但我不确定如何以有效的方式进行。我可以创建两个用于读取和写入的同步方法,但这显然会造成太多争用。

我想到的最佳解决方案是使用锁条带化。在执行任何 read/write 操作之前,会计算基于文件名的哈希值。然后,获取位置lockArr[hash % numOfLocks]的锁。 我还认为我应该使用 ReadWriteLocks,因为应该允许多个同时读取。

现在,这是我对问题的分析,我可能完全错了。对此有更好的解决方案吗?

锁定意味着有人必须等待其他人 -- 这不是最好的解决方案。

当客户端上传文件时,你应该把它写到同一磁盘上的一个临时文件中(通常在同一目录中),然后当文件上传完成时:

  1. 将旧版本重命名为临时名称。任何当前的读者都应该被迫关闭旧版本,重新打开临时版本,并寻找正确的位置。
  2. 将上传的文件重命名为目标文件名。
  3. 当任何读者使用完它时,删除旧文件的临时版本。

在典型的实现中,您需要一个集中式 class(我们称之为 ConcurrentFileAccessor)来管理线程之间的交互。

读者需要注册此 class,并在实际读取操作期间在某些对象上同步。上传完成后,作者必须声明所有这些锁以阻止读取、关闭所有读取的文件、重命名旧版本、重新打开、查找,然后释放它们以允许读者继续。