我有两个进程调用两个修改同一文本文件的Java程序。我注意到文本文件的内容缺少数据。我怀疑当一个java程序获取文本文件的写入流时,我认为它会阻止其他java程序修改它(比如当你打开文件时,你不能删除该文件)。有没有办法解决除数据库之外的其他问题? (并不是说数据库解决方案不干净或优雅,只是我们在操作此文本文件时写了很多代码)
修改
事实证明我针对这个问题犯了一个错误。我的文本文件中缺少数据的原因是,
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<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);
}
}
因此logList
是ProcessB
想要写出的当前列表。因此,在写出之前,我会读取文件并将数据存储到tempList
,如果tempList
和logList
不相同,请同步它们。问题是此时,ProcessA
和ProcessB
当前都访问该文件,因此当我尝试锁定文件并从中读取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();
}
}
}
答案 0 :(得分:4)
所以,你可以使用Vineet Reynolds在评论中建议的方法。
如果这两个进程实际上只是同一个应用程序中的单独线程,那么您可以在某处设置一个标志来指示该文件是打开的。
如果它是两个独立的应用程序/进程,则底层文件系统应该锁定文件。当您从输出流中获得I / O错误时,您应该能够围绕该输出流包装一个try / catch块,然后将您的应用程序设置为稍后重试,或者您的特定应用程序所需的任何行为。
文件并非真正设计为由多个应用程序同时写入。如果您可以描述为什么要同时从多个进程写入文件,可能会有其他可以建议的解决方案。
最近修改后的更新: 好的,所以你需要至少3个文件来做你正在谈论的事情。您绝对不能尝试同时读取/写入单个文件的数据。你的三个文件是:
ProcessB的循环:
ProcessA的循环:
答案 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心态来思考这个问题。让我们假设每个程序在不读取其他输出的情况下写入输出。我会写两个单独的文件,然后有一个'减少'阶段。您的减少可能是一个简单的按时间顺序排列的合并。
但是,如果你的节目需要一个人的输出。您有一个非常不同的问题,需要重新考虑如何对工作进行分区。
最后,如果两个程序的输出相似但是独立,并且您将其写入一个文件,以便第三个程序可以全部读取,请考虑更改第三个程序以读取这两个文件。