我有两个独立的线程F1和F2(准确地说,两个java.util.concurrent.FutureTask实例)并行运行。
F1进行一些处理,然后将结果复制到XML文件中。然后,它重复这些步骤,直到它无关(创建了许多XML文件)。 F2查看F1输出目录,取一个文件,解析它,并对其执行一些处理。
这非常有效,除了有时候F2从文件中获取截断的XML数据。我的意思是一个不完整的XML,其中一些XML节点不存在。问题是它并不总是可再现的,并且被截断的文件并不总是相同的。 因此,我认为当F1在磁盘上写入一个文件时,F2正在尝试读取同一个文件。这就是为什么有时我会遇到这种错误。
我的问题:我想知道是否有一些机制锁定(甚至是读取)文件F1当前正在写入,直到它完全完成将其写入磁盘,因此F2不会能够读取它直到文件解锁。或者欢迎任何其他方式来解决我的问题!
F1正在以这种方式编写文件:
try {
file = new File("some-file.xml");
FileUtils.writeStringToFile(file, xmlDataAsString);
} catch (IOException ioe) {
LOGGER.error("Error occurred while storing the XML in a file.", ioe);
}
F2正在以这种方式读取文件:
private File getNextFileToMap() {
File path = getPath(); // Returns the directory where F1 stores the results...
File[] files = path.listFiles(new FilenameFilter() {
public boolean accept(File file, String name) {
return name.toLowerCase().endsWith(".xml");
}
});
if (files.length > 0) {
return files[0];
}
return null;
}
// Somewhere in my main method of F2
...
f = getNextFileToMap();
Node xmlNode = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(f);
if (doc != null) {
xmlNode = doc.getDocumentElement();
}
} catch (Exception e) {
LOGGER.error("Error while getting the XML from the file " + f.getAbsolutePath(), e);
}
答案 0 :(得分:6)
由于您已经在F2中过滤了.xml
个文件,因此请将F1输出转换为.temp
文件,然后将其重命名为.xml
作为最后一步。这样,F2将忽略F1正在制作的文件,直到F1完全完成。
答案 1 :(得分:6)
您是否查看了java.nio.channels.FileLock
API?
一个更简单的解决方案可能是写入不同的文件名(例如foo.tmp),然后在它准备就绪时将其重命名(例如foo.xml) - 重命名在大多数操作系统上(在目录中)是原子的,所以当其他进程看到XML文件时,它应该是完整的。这可能比锁定简单得多。
答案 2 :(得分:2)
在公共对象上使用关键字synchronized,在这种情况下,指向该文件的文件对象将是最佳的:
class MyFileFactory {
private static HashMap<string,File> files = new HashMap<String,File>();
public static File get(String name) {
if (!files.keyExists(name)) {
files.put(name, new File(name));
}
return files.get(name);
}
}
// Another class
synchronized(File f = MyFileFactory::get("some-file.xml")) {
// read or write here
}