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

问题描述 投票:4回答:4

可能重复: 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);时,我要么得到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

java io
4个回答
4
投票

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

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

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

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


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

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

ProcessB的循环:

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

ProcessA的循环:

  • 将所有新的传入数据写入文件#1
  • 定期检查文件#2是否存在
  • 当Process删除文件#2时,Process应该停止写入文件#1,将文件#1重命名为文件#2
  • 开始一个新文件#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
投票

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

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

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

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

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

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


1
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.