Блокировка файла во время записи на диск - PullRequest
3 голосов
/ 30 марта 2009

У меня есть два независимых потока 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);
}

Ответы [ 3 ]

6 голосов
/ 30 марта 2009

Вы смотрели на java.nio.channels.FileLock API?

Более простым решением может быть запись в другое имя файла (например, foo.tmp), а затем переименование его (например, в foo.xml), когда оно будет готово - переименование является атомарным в большинстве операционных систем (в пределах каталога), поэтому, когда другой процесс видит файл XML, он должен быть завершен. Вероятно, это будет намного проще, чем блокировка.

6 голосов
/ 30 марта 2009

Поскольку вы уже фильтруете файлы .xml в F2, подготовьте вывод F1 в файл .temp, а затем переименуйте его в .xml в качестве последнего шага. Таким образом, F2 будет игнорировать файл, созданный F1, пока F1 полностью не завершит его.

2 голосов
/ 30 марта 2009

Используйте ключевое слово synchronized для общего объекта, в этом случае лучше всего подойдет объект File, указывающий на файл:

 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
 }
...