Подтвердить что ты не робот

Как атомно переименовать файл на Java, даже если файл dest уже существует?

У меня есть кластер машин, каждый из которых запускает приложение Java.

Эти приложения Java должны получить доступ к уникальному файлу resource.txt.

Мне нужно атомно переименовать файл temp.txt в resource.txt в Java, даже если resource.txt уже существует.

Удаление resource.txt и переименование temp.txt не работает, поскольку оно не является атомарным (он создает небольшой таймфрейм, где resource.txt не существует).

И он должен быть кросс-платформенным...

Спасибо!

4b9b3361

Ответ 2

В Linux (и я полагаю, что Solaris и другие операционные системы UNIX) метод Java File.renameTo() перезапишет целевой файл, если он существует, но это не относится к Windows.

Чтобы быть кросс-платформой, я думаю, вам придется использовать блокировку файлов на ресурсе .txt, а затем перезаписать данные.

Поведение блокировки файла зависит от платформы. На некоторых платформах, файловый замок является рекомендательным, что означает что, если приложение не проверяет блокировка файла, это не будет предотвращено от доступа к файлу. На других платформы, блокировка файлов обязательна, это означает, что блокировка файлов предотвращает любое приложение от доступа к файл.

try {
    // Get a file channel for the file
    File file = new File("filename");
    FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

    // Use the file channel to create a lock on the file.
    // This method blocks until it can retrieve the lock.
    FileLock lock = channel.lock();

    // Try acquiring the lock without blocking. This method returns
    // null or throws an exception if the file is already locked.
    try {
        lock = channel.tryLock();
    } catch (OverlappingFileLockException e) {
        // File is already locked in this thread or virtual machine
    }

    // Release the lock
    lock.release();

    // Close the file
    channel.close();
} catch (Exception e) {
}

Linux по умолчанию использует произвольную блокировку, в то время как Windows ее применяет. Возможно, вы можете обнаружить ОС и использовать renameTo() под UNIX с некоторым кодом блокировки для Windows?

Также есть возможность включить обязательную блокировку под Linux для определенных файлов, но это неясно. Вы должны установить бит режима как раз справа.

Linux, следуя Системе V (см. Систему Версия VID (версия SVID) 3), позволяет бит sgid для файлов без знака разрешения группы файл для обязательной блокировки

Ответ 4

Как указано здесь, похоже, что ОС Windows даже не поддерживает переименование атомного файла для более старых версий. Скорее всего, вам придется использовать некоторые механизмы ручного блокирования или какие-то транзакции. Для этого вы можете взглянуть на пакет apache commons transaction.

Ответ 5

Если это должно быть кросс-платформенная, я предлагаю 2 варианта:

  • Внедрить промежуточную службу, которая отвечает за все обращения к файлам. Здесь вы можете использовать несколько механизмов для синхронизации запросов. Каждое клиентское приложение java обращается к файлу только через эту службу.
  • Создайте управляющий файл каждый раз, когда вам нужно выполнить синхронизированные операции. Каждое приложение Java, которое обращается к файлу, отвечает за проверку файла управления и ожидает, пока этот управляющий файл существует. (почти как семафор). Процесс, выполняющий операцию удаления/переименования, отвечает за создание/удаление управляющего файла.

Ответ 6

Если целью переименования является замена ресурса .txt на лету, и вы контролируете все задействованные программы, а частота замены невелика, вы можете сделать следующее.

Чтобы открыть/прочитать файл:

  • Откройте "resource.txt", если это не сработает
  • Откройте "resource.old.txt" , если это не сработает
  • Откройте "resource.txt" еще раз, если это не сработает
  • У вас есть условие ошибки.

Чтобы заменить файл:

  • Переименуйте "resource.txt" в "resource.old.txt" , затем
  • Переименуйте "resource.new.txt" в "resource.txt", затем
  • Удалить "resource.old.txt" .

Это гарантирует, что все ваши читатели всегда найдут действительный файл.

Но проще, просто попробуй свое открытие в цикле, например:

InputStream inp=null;
StopWatch   tmr=new StopWatch();                     // made up class, not std Java
IOException err=null;

while(inp==null && tmr.elapsed()<5000) {             // or some approp. length of time
    try { inp=new FileInputStream("resource.txt"); }
    catch(IOException thr) { err=thr; sleep(100); }  // or some approp. length of time
    }

if(inp==null) {
     // handle error here - file did not turn up after required elapsed time
     throw new IOException("Could not obtain data from resource.txt file");
     }

... carry on

Ответ 7

Вы можете получить некоторую тягу, установив блокировку filechannel в файле перед ее переименованием (и удалив файл, который вы собираетесь перезаписать после блокировки). -r