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

时间:2011-06-13 15:20:26

标签: java io

  

可能重复:
  How can I lock a file using java (if possible)

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

修改

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

ProcessA:不断向文本文件中添加数据行

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

这项工作在顺序过程中很棒。但是,当一起运行时,如果ProcessA向文件中添加数据,则在ProcessB操纵List期间,当ProcessBList写回时,无论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<PackageLog> 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想要写出的当前列表。因此,在写出之前,我会读取文件并将数据存储到tempList,如果tempListlogList不相同,请同步它们。问题是此时,ProcessAProcessB当前都访问该文件,因此当我尝试锁定文件并从中读取List<PackageLog> tempList = readAllLogs(file);时,我得到{{1} },或OverlappingFileLockException。请帮我解决这个问题:(

EDIT2 :我对Lock的理解

java.io.IOException: The process cannot access the file because another process has locked a portion of the file

我收到此错误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(); } } }

4 个答案:

答案 0 :(得分:4)

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

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

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

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


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

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

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

    ProcessA的循环:

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

答案 1 :(得分:2)

如果这是两个单独的应用程序试图访问该文件。那个人会通过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意味着它进入太深;但是在这种情况下发生这种情况的可能性很小 - 只有在文件被其他应用程序长时间保持打开时才会发生。

希望这有帮助!

答案 2 :(得分:2)

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

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

  

如果锁定与请求重叠   这个Java已经拥有了这个区域   虚拟机,或者如果是另一个线程   已经在此方法中被阻止了   试图锁定重叠   同一文件的区域

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

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

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

答案 3 :(得分:1)

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

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

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