多线程访问Java文件

时间:2019-02-07 13:00:00

标签: java multithreading file server filechannel

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

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

要进行传输,我计划通过transferFrom和transferTo方法使用FileChannels和SocketChannels。根据文档,这两种方法是线程安全的。 事实是,仅调用这两个函数不足以完全读取/写入文件。

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

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

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


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

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

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

1 个答案:

答案 0 :(得分:3)

锁定意味着有人必须等待别人-并非最佳解决方案。

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

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

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

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