Как избежать прерывистых пустых строк при использовании 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
выход.)