Блокировка Java-файлов в сети - PullRequest
11 голосов
/ 06 января 2009

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

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

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

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

Ответы [ 4 ]

6 голосов
/ 06 января 2009

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

4 голосов
/ 20 октября 2012

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

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

В нем говорится:

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

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

3 голосов
/ 06 января 2009

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

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

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

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

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();
    }
1 голос
/ 06 января 2009

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

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

...