У меня есть каталог, содержащий файлы данных, обслуживаемые клиентами, например /srv/data
. Выполняя серию обновлений, я работаю над /srv/data_tmp
, и в конце операции я хотел бы атомарно заменить data
на data_tmp
. File.renameTo()
для меня всегда возвращает false, если местом назначения является существующий каталог. Как я могу это сделать?
Как атомарно заменить каталог другим в Java?
Ответы (4)
Боюсь, ты не сможешь. По крайней мере, не на уровне SO. Таким образом, даже если вы управляете «атомарностью» в контексте вашего java-приложения, у вас нет гарантии, что какой-то другой «мошеннический» процесс вмешивается на фактический уровень файловой системы.
На вашем месте я бы прочитал эту статью (довольно старый, но должен дать вам некоторые идеи), а затем посмотрите, сможете ли вы перенести предложенный подход на более современная версия.
Ой, подождите, кто-то уже сделал это!
И, очевидно, вы не первый, кто задает здесь вопрос,
Удачи...
Вы можете заменить каталог /srv/data
символической ссылкой (или переходом в Windows XP) и при необходимости измените цель ссылки. Однако вы не сможете сделать это с помощью API Java 6 - вам придется полагаться на библиотеку или самостоятельно писать команды командной строки.
NB: Я не гарантирую ничего относительно атомарности этой операции.
/home/me
, тогда это успешно заменяет b на a (в Mac OS X / HFS +): new File("/home/me/a").renameTo(new File("/home/me/b")
- person Jean-Philippe Pellet; 08.12.2010
Системный вызов rename
Linux не позволяет этого (системный вызов rename
может перезаписать только пустой каталог), поэтому я сомневаюсь, что это возможно сделать на Java в Linux.
rename
атомарным? И если да, то также в случае перезаписи?
- person Jean-Philippe Pellet; 08.12.2010
Достижение этой цели вполне возможно с помощью комбинации «символической ссылки» и «переименования» вместе с промежуточным каталогом tmp. Следующий пример находится в оболочке, но вы можете легко перевести функциональность здесь, чтобы использовать базовые вызовы:
mkdir -p tmp/real_dir1 tmp/real_dir2
touch tmp/real_dir1/a tmp/real_dir2/a
# start with ./target_dir pointing to tmp/real_dir1
ln -s tmp/real_dir1 target_dir
# create a symlink named target_dir in tmp, pointing to real_dir2
ln -sf tmp/real_dir2 tmp/target_dir
# atomically mv it into ./ replacing ./target_dir
mv tmp/target_dir ./
Пример взят из: http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/
Это сводится к (в псевдокоде):
mkdir('./tmp');
mkdir('./tmp/real_dir1');
mkdir('./tmp/real_dir2');
symlink('./tmp/real_dir1', './target_dir')
symlink('./tmp/real_dir2', './tmp/target_dir')
rename('./tmp/target_dir', './target_dir')
Окончательное переименование здесь является атомарным, поэтому действие будет либо успешным, либо полностью провальным, с точки зрения любого процесса, использующего каталог, действие является атомарным.