Java:如何处理两个进程试图修改同一个文件

可能重复:
如何使用java锁定文件(如果可能)

我有两个进程调用两个修改同一文本文件的Java程序。 我注意到文本文件的内容缺少数据。 我怀疑当一个java程序获取文本文件的写入流时,我认为它会阻止其他java程序修改它(比如当你打开文件时,你不能删除该文件)。 有没有办法解决除数据库之外的其他问题? (并不是说数据库解决方案不干净或优雅,只是我们在操作此文本文件时写了很多代码)

编辑

事实certificate,我犯了一个针对这个问题的错误。 我的文本文件中缺少数据的原因是,

ProcessA :继续向文本文件添加数据行

ProcessB :在开头,将文本字段的所有行加载到List 。 然后它操纵该列表的包含。 最后, ProcessB将列表写回,替换文本文件的包含。

这项工作在顺序过程中很棒。 但是当一起运行时,如果ProcessA将数据添加到文件中,在ProcessB操作List ,那么当ProcessB写回List时,无论ProcessA刚添加什么,都将被覆盖。 所以我最初的想法是在ProcessB写回List之前,在文本文件和List之间同步数据。 因此,当我将List写回时,它将包含所有内容。 所以这是我的努力

 public void synchronizeFile(){ try { File file = new File("path/to/file/that/both/A/and/B/write/to"); FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); FileLock lock = channel.lock(); //Lock the file. Block until release the lock List tempList = readAllLogs(file); if(tempList.size() > logList.size()){ //data are in corrupted state. Synchronized them. for(PackageLog pl : tempList){ if(!pl.equals(lookUp(pl.getPackageLabel().getPackageId(), pl.getPackageLabel().getTransactionId()))){ logList.add(pl); } } } lock.release(); //Release the file channel.close(); } catch (IOException e) { logger.error("IOException: ", e); } } 

因此logListProcessB想要写出的当前List。 所以在写出之前,我读取文件并将数据存储到tempList ,如果tempListlogList不相同,则同步它们。 问题是,此时ProcessAProcessB当前都访问该文件,所以当我尝试锁定文件时,从中读取List tempList = readAllLogs(file); ,我要么得到OverlappingFileLockException ,要么得到java.io.IOException: The process cannot access the file because another process has locked a portion of the file 。 请帮我解决这个问题:(

EDIT2 :我对Lock的理解

 public static void main(String[] args){ File file = new File("C:\\dev\\harry\\data.txt"); FileReader fileReader = null; BufferedReader bufferedReader = null; FileChannel channel = null; FileLock lock = null; try{ channel = new RandomAccessFile(file, "rw").getChannel(); lock = channel.lock(); fileReader = new FileReader(file); bufferedReader = new BufferedReader(fileReader); String data; while((data = bufferedReader.readLine()) != null){ System.out.println(data); } }catch(IOException e){ e.printStackTrace(); }finally{ try { lock.release(); channel.close(); if(bufferedReader != null) bufferedReader.close(); if(fileReader != null) fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } 

我收到此错误IOException: The process cannot access the file because another process has locked a portion of the file

所以,你可以使用Vineet Reynolds在评论中建议的方法。

如果这两个进程实际上只是同一个应用程序中的单独线程,那么您可以在某处设置一个标志来指示该文件是打开的。

如果它是两个独立的应用程序/进程,则底层文件系统应该锁定文件。 当您从输出流中获得I / O错误时,您应该能够围绕该输出流包装一个try / catch块,然后将您的应用程序设置为稍后重试,或者为您的特定应用程序设置所需的行为。

文件实际上并非设计为由多个应用程序同时写入。 如果您可以描述为什么要同时从多个进程写入文件,可能会有其他可以建议的解决方案。


最近编辑后的更新:好的,所以你需要至少3个文件来做你正在谈论的内容。 您绝对不能尝试同时读取/写入单个文件的数据。 你的三个文件是:

  1. ProcessA将新数据/传入数据转储到的文件
  2. ProcessB当前正在处理的文件
  3. 保存ProcessB输出的最终“输出”文件。

ProcessB的循环:

  • 获取文件#2中的任何数据,处理它,并将输出写入文件#3
  • 删除文件#2
  • 重复

ProcessA的循环:

  • 将所有新的传入数据写入文件#1
  • 定期检查文件#2是否存在
  • 当ProcessB删除文件#2时,ProcessA应该停止写入文件#1,将文件#1重命名为文件#2
  • 开始一个新文件#1
  • 重复

如果这是两个单独的应用程序试图访问该文件。 那个人会通过IOException,因为他无法访问它。 如果发生这种情况,在catch(IOException err){}添加代码以暂停当前线程几毫秒,然后递归尝试再次写入 – 直到它获得访问权限。

 public boolean writeFile() { try { //write to file here return true; } catch (IOException err) // Can't access { try { Thread.sleep(200); // Sleep a bit return writeFile(); // Try again } catch (InterruptedException err2) { return writeFile(); // Could not sleep, try again anyway } } } 

这将继续尝试,直到你得到一个StackOverflow Exception意味着它进入太深; 但是在这种情况下发生这种情况的可能性很小 – 只有当文件被其他应用程序保持打开很长时间时才会发生。

希望这可以帮助!

更新问题中的代码很可能是流程B中的代码,而不是流程A中的代码。我将假设情况就是这样。

考虑到抛出OverlappingFileLockExceptionexception的实例,似乎同一进程中的另一个线程试图锁定同一文件。 这不是A和B之间的冲突,而是B中的冲突,如果通过lock()方法的API文档以及它抛出OverlappingFileLockException的条件:

如果此Java虚拟机已经拥有与所请求区域重叠的锁,或者此方法中已阻止另一个线程并且正在尝试锁定同一文件的重叠区域

防止这种情况的唯一解决方案是阻止B中的任何其他线程获取对同一文件或文件中相同重叠区域的锁定。

抛出的IOException有一些更有趣的消息。 它可能证实了上述理论,但没有看完整个源代码,我无法确认。 lock方法预计会阻塞,直到获得独占锁定。 如果它被获得,那么写入文件应该没有问题。 除了一个条件。 如果文件已被同一JVM在另一个线程中打开(并锁定),使用File对象(或者换句话说,第二个/不同的文件描述符),则对第一个文件描述符的尝试写入将失败如果获得了锁(毕竟,锁不会锁定其他线程)。

一种改进的设计,就是在每个进程中有一个单独的线程获取文件的独占锁(当使用单个File对象或单个文件描述符)仅一段时间时,执行所需的活动。文件,然后释放锁。

使用MapReduce心态来考虑这一点。 让我们假设每个程序在不读取其他输出的情况下写入输出。 我会写两个单独的文件,然后有一个’减少’阶段。 您的减少可能是一个简单的按时间顺序排列的合并。

但是,如果你的节目需要一个人的输出。 您有一个非常不同的问题,需要重新考虑如何分区工作。

最后,如果两个程序的输出相似但是独立,并且您将它写入一个文件,以便第三个程序可以全部读取,请考虑更改第三个程序以读取这两个文件。