Как избежать прерывистых пустых строк при использовании FileOutputStream с append = false?

Следующий код иногда оставляет random.txt файл без содержимого:

final Random rand = new Random();

while (true) {
    try (FileOutputStream fos = new FileOutputStream("random.txt", false);
         PrintWriter pw = new PrintWriter(fos, true)) {
        pw.println(rand.nextLong());
    } catch (IOException ioe) {
        ioe.printStackTrace();
    }
}

Это можно проверить с помощью tail:

$ tail -f random.txt
511109499422519327

-4120669548002912852
-6691981558077108749
-3630360891256027458
2917713483009724854
-5999569794311404435
-7616466037397807657
6997723694593334477
-7350203015863330163
1355067773463270538
-8140835511024500423
-2536681669468724500
-2926317178660145957
-6983787629710676243
7119127016459332336

7186294589323134873
-8389505833590104437
197327637272321424
-1458700265861408851
5685819798785563231
4060059342974359248
215297222019419003
2913123181036087590
-5940005294121482941
5658270253202816998

Как вы можете видеть, есть два пробела в tailвыходной. Это происходит потому, что FileOutputStream(fileName, false) сначала усекает файл, а затем записывает в него новые данные.

Так что если tail случается, что содержимое файла читается правильно, когда он усекается, он показывает пустую строку.

Кроме использования методов, которые используют несколько файлов и т. Д., Есть ли способ убедиться, что операции усечения и записи выполняются атомарно?

1 ответ

Решение

Кроме использования методов, которые используют несколько файлов и т. Д., Есть ли способ убедиться, что операции усечения и записи выполняются атомарно?

Нет. На базовом уровне усечение и запись в файл являются двумя отдельными системными вызовами, и ОС не обеспечивает способ объединения двух системных вызовов в элементарную операцию (или транзакцию).

Если вы хотите найти крайний случай для tail -f тогда вы добились успеха. В противном случае, возможно, есть лучшие / более эффективные способы сделать то, что вы пытаетесь сделать здесь... например, запись в канал или добавление в файл. И если есть какая-то конкретная причина, по которой вам нужно это сделать, рассмотрите возможность фильтрации пустых строк.

На самом деле, я обеспокоен тем, что здесь может быть проблема хуже, чем случайные пустые строки. Возможно, что вы также теряете непустые строки, если Java-программа усекает файл раньше tail прочитал последнюю строку. (Попробуйте заменить rand.nextLong() со значением возрастающего счетчика... и посмотрите, видите ли вы все числа в tail выход.)

Другие вопросы по тегам