Невозможно заблокировать файлы в Linux с помощью java.НИО.каналы.Забыл
Я создавал приложение на Java,для которого мне нужен только один экземпляр. Для этого я создал файл и получил блокировку, пока мое приложение работает.
У меня есть следующий код, который работает в Windows, но не работает в Linux: как только я получаю блокировку, не отпирая ее, я могу получить другую блокировку.
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
public class MyApp {
private static File f;
private static FileChannel channel;
private static FileLock lock;
public static void main(String[] args) {
try {
f = new File("RingOnRequest.lock");
// Check if the lock exist
if (f.exists()) {
// if exist try to delete it
f.delete();
}
// Try to get the lock
channel = new RandomAccessFile(f, "rw").getChannel();
lock = channel.tryLock();
if(lock == null)
{
// File is lock by other application
channel.close();
throw new RuntimeException("Only 1 instance of MyApp can run.");
}
// Add shutdown hook to release lock when application shutdown
ShutdownHook shutdownHook = new ShutdownHook();
Runtime.getRuntime().addShutdownHook(shutdownHook);
//Your application tasks here..
System.out.println("Running");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
catch(IOException e)
{
throw new RuntimeException("Could not start process.", e);
}
}
public static void unlockFile() {
// release and delete file lock
try {
if(lock != null) {
lock.release();
channel.close();
f.delete();
}
} catch(IOException e) {
e.printStackTrace();
}
}
static class ShutdownHook extends Thread {
public void run() {
unlockFile();
}
}
}
6 ответов:
Я использовал тот же пример, что и вы, и получил ту же проблему на Mac OS X. Похоже, что блокировка файлов не предотвращает удаление файлов в системах POSIX . У вашего приложения все еще будет какой-то дескриптор к этому файлу, пока вы его не разблокируете. Поэтому рассмотрите возможность использования файла блокировки с PID в его имени( или внутри файла).
Вы удаляете файл блокировки при каждом запуске,поэтому только один процесс может иметь блокировку.
Когда вы используете
FileLock
, это чисто консультативный-получение блокировки на файл не может остановить вас от чего-либо...чтение, запись и удаление файла могут быть возможны даже тогда, когда другой процесс получил блокировку. Иногда блокировка может делать больше, чем это на определенной платформе, но это поведение не определено, и полагаться на большее, чем гарантировано в классе документация-это рецепт неудачи."консультативная блокировка" - этотолько сигнал, который виден другим процессам, которые утруждают себя его поиском. Если вы зависите от него больше, чем это, ваша программа сломается при запуске на какой-то другой платформе.
Зачем вам вообще удалять файл блокировки? Файл блокировки подобен логическому флагу, который виден каждому процессу в системе. Разработайте свой протокол, чтобы использовать его таким образом, и у вас будет надежная кросс-платформенная блокировка механизм.
Почему бы вам не сохранить PID в файл и вместо блокировки файла проверить, есть ли процесс с этим идентификатором. Если есть, и это экземпляр вашего приложения, вы знаете, что оно уже запущено.
Сокет также может быть хорошей идеей, так как вы можете использовать его для связи с запущенным экземпляром.
Редактировать:
Также, из javadoc FileLock :
Действительно ли блокировка препятствует работе другой программы? подключение содержание заблокированной области зависит от системы и поэтому неопределенный.
Используйте
mkdir
. В системах unix это атомарная операция – она будет успешной, если новый каталог будет успешно создан, в противном случае она завершится неудачей.Пример:
File lockFile = new File("/path/to/lockdir"); boolean hasLock = lockFile.mkdir(); if (!hasLock) { throw new IOException("could not get lock"); } // do stuff lockFile.delete();
Я протестировал его как на Windows, так и на Linux. Работать отлично. Файл блокировки удаляется автоматически, когда приложение закрывается нормально. Так что вам не придется беспокоиться о том, что файл блокировки останется там, когда вы перезагрузите приложение. Просто прокомментируйте следующие строки:
Тем не менее, вы можете рассмотреть, что произойдет, если ваше приложение завершит работу и не закроется обычным образом.if (f.exists()) { // if exist try to delete it f.delete(); }
Недавно я столкнулся с такой же проблемой, но в моем случае у меня было преимущество: мое приложение опрашивало некоторый каталог только после некоторого таймаута. Поскольку мое приложение не сразу опросило каталог, я написал специальный класс, который создает файл блокировки со своим собственным PID внутри в методе init, после чего, прежде чем он попытается работать с каталогом, он должен вызвать
ownedLock()
- если он возвращает true, то мы можем работать иначе exit (код находится в Kotlin, но вы получите основную идею):import java.io.File import java.lang.management.ManagementFactory class DirectoryLocker(private val directory: String, private val lockName: String) { private val lockFile by lazy { File("$directory/$lockName.lock") } // Will try to acquire lock to directory, whoever last writes its pid to file owns the directory fun acquireLock() = with(lockFile) { createNewFile() writeText(currentPID()) } fun ownedLock(): Boolean = lockFilePid() == currentPID() fun releaseOwnedLock() { if(lockFilePid() == currentPID()) lockFile.delete() } private fun currentPID(): String { val processName = ManagementFactory.getRuntimeMXBean().name return processName.split("@".toRegex()).first() } private fun lockFilePid(): String? { return if(lockFile.exists()) lockFile.readLines().first() else null } }