Предотвращение запуска нескольких экземпляров Java-приложения с помощью блокировки файла не работает

Вопрос или проблема

Почему-то это решение для предотвращения запуска нескольких экземпляров приложения больше не работает – в чем проблема?

Я ожидаю, что при повторном запуске этого приложения будет выводиться: “другой экземпляр запущен” и оно завершится с ошибкой, но этого не происходит. Второй экземпляр работает так же, как и первый.

import java.io.File;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.StandardOpenOption;

public class JustOneTest {

    public static void main(String[] args) {
        runCheck();
        while (true) {
            try {
                System.out.print(".");
                Thread.sleep(5 * 60);
            } catch (Exception e) {
                    e.printStackTrace();
            }
        }
    }
    
    private static void runCheck() {
        System.out.println("начинаем проверку: приложение запущено");
        final File file = new File(System.getProperty("user.home"), "khf.lock");

        try {
            FileLock lock;
            
            try (FileChannel fc = FileChannel.open(file.toPath(), 
                    StandardOpenOption.CREATE,
                    StandardOpenOption.WRITE)) {
                lock = fc.tryLock();
            }

            if (lock == null) {
                System.out.println("другой экземпляр запущен");
                throw new Error("другой экземпляр запущен");
            }

            System.out.println(lock);
            // вывод: sun.nio.ch.FileLockImpl[0:9223372036854775807 exclusive invalid]
            
        } catch (IOException e) {
            throw new Error(e);
        }
        
        System.out.println("проверка завершена");
    }
  
}

Ответ или решение

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

Давайте разберемся с проблемами и возможными решениями:

  1. Отсутствие правильной блокировки:
    В вашем коде блокировка создается и закрывается сразу после вызова tryLock(), что не сохраняет блокировку, поскольку FileChannel закрывается до того, как блокировка может быть использована. Вам нужно держать FileChannel открытым, пока блокировка активна. Вот как можно исправить это:

    private static void runCheck() {
       System.out.println("start check: is app running");
       final File file = new File(System.getProperty("user.home"), "khf.lock");
    
       FileChannel fc = null;
       FileLock lock = null;
    
       try {
           fc = FileChannel.open(file.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
           lock = fc.tryLock();
    
           if (lock == null) {
               System.out.println("another instance is running");
               throw new Error("another instance is running");
           }
    
           System.out.println(lock);
    
       } catch (IOException e) {
           throw new Error(e);
       } finally {
           // Закрываем канал и блокировку в конце
           try {
               if (lock != null) {
                   lock.release();
               }
               if (fc != null) {
                   fc.close();
               }
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
    
       System.out.println("start check completed");
    }
  2. Проблемы с доступом к файлу:
    Убедитесь, что у вашего приложения есть права на создание и запись в файл блокировки в указанной директории. Если ваше приложение работает от имени пользователя, у которого нет прав на запись в user.home, блокировка не сработает.

  3. Платформозависимость:
    Имейте в виду, что поведение блокировки файлов может различаться в зависимости от операционной системы. Например, блокировки, установленные на одном потоке, могут не работать на других потоках в некоторых средах выполнения. Поэтому убедитесь, что вы тестируете это в подходящей среде.

  4. Закрытие ресурсов:
    Убедитесь, что ресурсы закрываются корректно, даже в случае возникновения исключений. Использование блоков try-with-resources для файловых каналов и блокировок может помочь с правильным управлением ресурсами.

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

Оцените материал
Добавить комментарий

Капча загружается...