Блокировка файлов Java в сети

Возможно, это похоже на предыдущие сообщения, но я хочу быть конкретным относительно использования блокировки в сети, а не локально. Я хочу записать файл в разделяемое место, поэтому он вполне может перейти в сеть (конечно, в сеть Windows, возможно, Mac). Я хочу, чтобы другие люди не читали какую-либо часть этого файла, пока он был написан. Это не будет очень параллельным процессом, и файлы будут обычно меньше 10 МБ.

Я прочитал документацию FileLock и документацию File, и я немного смущен, что касается того, что безопасно, а что нет. Я хочу заблокировать весь файл, а не его части.

Могу ли я использовать FileChannel.tryLock(), и он безопасен в сети или зависит от типа сети? Будет ли он работать в стандартной сети Windows (если есть такая вещь).

Если это не сработает, лучше всего создать нулевой файл или каталог в виде файла блокировки, а затем выписать основной файл. Почему в этой документации File.createNewFile() не используется это для блокировки файлов? Я понимаю, что это зависит от условий гонки и не является идеальным.

Ответ 1

Это не может быть надежно выполнено в сетевой файловой системе. Пока ваше приложение является единственным приложением, которое обращается к файлу, лучше всего реализовать какой-то совлокальный процесс блокировки (возможно, запись файла блокировки в сетевую файловую систему при открытии файла). Причина, по которой это не рекомендуется, заключается в том, что если ваш процесс выйдет из строя или сеть опустится или произойдет любое другое количество проблем, ваше приложение попадет в неприятное грязное состояние.

Ответ 2

Я нашел этот отчет об ошибке, в котором описывается, почему примечание о блокировке файлов было добавлено в документацию File.createNewFile.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4676183

В нем указано:

Если вы помечаете файл как deleteOnExit перед вызовом createNewFile, но файл уже существует, вы рискуете удалить файл, который вы не создали, и удалить кого-то блокировку elses! С другой стороны, если вы помечаете файл после его создания, вы теряете атомарность: если программа выйдет до того, как файл будет отмечен, он не будет удален и блокировка будет "заклинирована".

Таким образом, похоже, что основной причиной блокировки является отказ от файла File.createNewFile() в том, что вы можете закончить с потерянными файлами блокировки, если JVM неожиданно завершится, прежде чем у вас будет возможность его удалить. Если вы можете иметь дело с сиротскими файлами блокировки, то его можно использовать как простой механизм блокировки. Тем не менее, я бы не рекомендовал метод, предложенный в комментариях к отчету об ошибке, поскольку он имеет условия гонки вокруг чтения/записи значения метки времени и исправления истекшей блокировки.

Ответ 3

У вас может быть пустой файл, который лежит на сервере, который вы хотите записать.

Когда вы хотите писать на сервер, вы можете поймать токен. Только когда у вас есть токен, вы должны писать в любой файл, который лежит на сервере.

Когда вы будете готовы к работе с файлами или выбрано исключение, вы должны освободить токен.

Класс-помощник может выглядеть как

private FileLock lock;

private File tokenFile;

public SLTokenLock(String serverDirectory) {
    String tokenFilePath = serverDirectory + File.separator + TOKEN_FILE;
    tokenFile = new File(tokenFilePath);
}

public void catchCommitToken() throws TokenException {
    RandomAccessFile raf;
    try {
        raf = new RandomAccessFile(tokenFile, "rw"); //$NON-NLS-1$
        FileChannel channel = raf.getChannel();
        lock = channel.tryLock();

        if (lock == null) {
            throw new TokenException(CANT_CATCH_TOKEN);
        }
    } catch (Exception e) {
        throw new TokenException(CANT_CATCH_TOKEN, e);
    }
}

public void releaseCommitToken() throws TokenException {
    try {
        if (lock != null && lock.isValid()) {
            lock.release();
        }
    } catch (Exception e) {
        throw new TokenException(CANT_RELEASE_TOKEN, e);
    }
}

Ваши действия должны выглядеть как

try {
        token.catchCommitToken();

        // WRITE or READ to files inside the directory
    } finally {
        token.releaseCommitToken();
    }

Ответ 4

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

Недостатком является то, что скрытие и/или переименование без дополнительного ввода-вывода может потребовать от вас использования собственных команд ОС, но процедура для этого должна быть довольно простой и детерминированной.