Вопрос

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

Решение

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

Другие советы

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

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

Используйте ключевое слово синхронизированный для общего объекта лучше всего в этом случае будет объект 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
 }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top