Блокировка файлов 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
Вместо реализации стратегии блокировки, которая, по всей вероятности, будет полагаться на читателей, чтобы придерживаться вашего соглашения, но не заставит их, возможно, вы можете записать файл в скрытый или неясно названный файл, где это будет эффективно невидимым для читателей. Когда операция записи будет завершена, переименуйте файл в ожидаемое публичное имя.
Недостатком является то, что скрытие и/или переименование без дополнительного ввода-вывода может потребовать от вас использования собственных команд ОС, но процедура для этого должна быть довольно простой и детерминированной.