Невозможно заблокировать файлы в 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 3

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
   }
}