Является ли операция перемещения в атоме Unix?
Предположим, что есть 2 процесса P1 и P2, и они обращаются к общему файлу Foo.txt
.
Предположим, что P2 читается с Foo.txt
. Я не хочу, чтобы P1 писал на Foo.txt
, а P2 читает его.
Итак, я думал, что могу записать P1 на Foo.tmp
и в качестве последнего шага переименуем Foo.tmp
в Foo.txt
. Мой язык программирования - Java
Итак, мой вопрос: обеспечит ли это то, что P2 считывает правильные данные из Foo.txt
? Будет ли операция переименования выполнена после того, как P2 завершит чтение файла?
ИЗМЕНИТЬ
Я попытался воссоздать этот сценарий следующим образом:
Мой код P1 выглядит примерно так:
File tempFile = new File(path1);
File realFile = new File(path2);
BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
for(int i=0;i<10000;i++)
writer.write("Hello World\n");
writer.flush();
writer.close();
tempFile.renameTo(realFile);
и мой код P2:
BufferedReader br = new BufferedReader(new FileReader(file));
String line = null;
while(true) {
while((line=br.readLine())!=null){
System.out.println(line);
Thread.sleep(1000);
}
br.close();
}
Мой примерный общий файл:
Test Input
Test Input
Test Input
Я начинаю P1 и P2 почти одновременно (сначала P2).
Итак, согласно моему пониманию, хотя P1 написал новый Foo.txt, поскольку P2 уже его читает, он должен прочитать старый контент Foo.txt, пока он не откроет BufferedReader в Foo.txt.
Но на самом деле P2 читает Test Input
трижды, как ожидается от ввода, но после этого он считывает новое содержимое, которое было написано P1.
Выход из P2:
Test Input
Test Input
Test Input
Hello World
Hello World
Hello World
.
.
.
Так что это не работает так, как должно. Я неправильно тестирую этот сценарий? Я чувствую, что там что-то не хватает.
Ответы
Ответ 1
Операция UNIX rename
является атомарной (см. переименование (2)). Команда UNIX mv
использует переименование, если исходный и целевой путь находятся на одном физическом устройстве. Если целевой путь находится на другом устройстве, переименование не будет выполнено, а mv
скопирует файл (который не является атомарным).
Если путь к целевому файлу существует, rename
автоматически удалит его из файловой системы и заменит его новым файлом. Файл фактически не будет удален до тех пор, пока его количество ссылок не упадет до нуля, поэтому, если другой процесс в настоящее время читает файл, он будет продолжать читать старый файл. Как только все процессы закрыли старый файл, его счетчик ссылок упадет до нуля, и пространство для хранения файлов будет восстановлено.
Ответ 2
почему бы не использовать FileChannel.lock
?
вот пример:
http://examples.javacodegeeks.com/core-java/nio/filelock/create-shared-file-lock-on-file/
Ответ 3
- move (rename) является атомарным, если выполняется на том же устройстве. (устройство = тот же диск/раздел)
- Если
Foo.txt
завершает перемещение Foo.tmp
в Foo.txt
, скорее всего, произойдет сбой. (Но если вы сначала удалите Foo.txt
, а затем переместите, он должен работать). Случается, что файл физически не удаляется до тех пор, пока все обработчики файлов не будут закрыты (нет процесса, который использует этот файл). Кроме того, после оставшихся Foo.tmp
до Foo.txt
у вас будет 2 файла Foo.txt. Тот, который удален, но все еще открыт в памяти (в основном, этот файл больше не содержит ссылки на диске) и тот, который на самом деле находится на диске.
- Но после перемещения во втором процессе вам нужно снова открыть файл.
Сообщите мне, если мы находимся на одной странице с № 1.