java - Java中的多线程访问文件

标签 java multithreading file server filechannel

我正在使用 Java 开发多线程服务器。 服务器监视文件目录。客户端可以询问服务器:

  • 从服务器目录下载文件
  • 将现有文件的新版本上传到服务器,覆盖服务器目录中的旧版本。

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

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

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

在这种情况下,下载客户端会收到旧版本的一部分和新版本的一部分。

<小时/>

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

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

现在,这是我对问题的分析,我可能完全错误。有没有更好的解决办法?

最佳答案

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

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

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

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

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

关于java - Java中的多线程访问文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54573948/

相关文章:

java - 使用java线程进行互斥

java - 如何对文件夹应用正确的权限 "read"?

java - Spring Cloud Stream与RabbitMQ Binder,如何应用@Transactional?

java - 从弃用的 Blobstore File API 到服务 blob

c - TCL 正则表达式是否跨解释器共享 - TclReExec 程序以信号 11,段错误终止?

multithreading - Lua:Lua 中具有线程结构的抢占式(非合作式)多任务处理

c# - 如何在 C# 中读取自定义文件属性

arrays - 无法在Powershell中将字符数组写入文件

java - 如何使用相同元素和其他元素映射 XML Wrapper

java - 可以从 JDBC 中运行 sqlite .import 命令吗?