Как разрешить запуск только одного экземпляра Java-программы одновременно?

StackOverflow https://stackoverflow.com/questions/920386

  •  06-09-2019
  •  | 
  •  

Вопрос

Мне нужно запретить пользователям запускать мое Java-приложение (WebStart Swing app) несколько раз.Таким образом, если приложение уже запущено, должно быть невозможно запустить его снова или показать предупреждение / снова закрыть.

Есть ли какой-нибудь удобный способ добиться этого?Я думал о блокировке порта или записи sth в файл.Но, надеюсь, вы можете получить доступ к некоторым системным свойствам или JVM?

кстати.целевая платформа - Windows XP с Java 1.5

Это было полезно?

Решение

Я думаю, что ваше предложение открыть порт для прослушивания при запуске приложения — лучшая идея.

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

Кроме того, если я правильно помню, нет простого способа получить PID процесса Java изнутри JVM, поэтому не пытайтесь сформулировать решение с использованием PID.

Что-то вроде этого должно помочь:

private static final int PORT = 9999;
private static ServerSocket socket;    

private static void checkIfRunning() {
  try {
    //Bind to localhost adapter with a zero connection queue 
    socket = new ServerSocket(PORT,0,InetAddress.getByAddress(new byte[] {127,0,0,1}));
  }
  catch (BindException e) {
    System.err.println("Already running.");
    System.exit(1);
  }
  catch (IOException e) {
    System.err.println("Unexpected error.");
    e.printStackTrace();
    System.exit(2);
  }
}

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

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

Другие советы

Поскольку в вопросе указано, что используется WebStart, очевидным решением является использование javax.jnlp.SingleInstanceService.

Эта услуга доступна в версии 1.5.Обратите внимание, что срок службы версии 1.5 в настоящее время почти прошел.Получите Java SE 6!

Я думаю, что лучшей идеей было бы использовать блокировку файлов (довольно старая идея :) ).Начиная с Java 1.4, была введена новая библиотека ввода-вывода, которая позволяет блокировать файлы.

Как только приложение запускается, оно пытается получить блокировку файла (или создать его, если он не существует), при выходе приложения блокировка снимается.Если приложение не может получить блокировку, оно завершает работу.

Пример того, как выполнить блокировку файлов, приведен, например, в Альманах разработчиков Java.

Если вы хотите использовать блокировку файлов в Java Web Start application или апплете, вам нужно запустить приложение или апплет.

Мы делаем то же самое в C++, создавая объект-мьютекс ядра и ищем его при запуске.Преимущества те же, что и при использовании сокета, т.е. когда процесс умирает/падает/завершается/уничтожается, объект мьютекса очищается ядром.

Я не Java-программист, поэтому не уверен, сможете ли вы сделать то же самое на Java?

Вы можете использовать библиотеку JUnique.Он обеспечивает поддержку запуска одноэкземплярного Java-приложения и имеет открытый исходный код.

http://www.sauronsoftware.it/projects/junique/

См. также мой полный ответ на Как реализовать одноэкземплярное Java-приложение?

Я создал кросс-платформенный класс AppLock.

http://rumatoest.blogspot.com/2015/01/howto-run-only-single-java-application.html

Он использует технику блокировки файлов.

Обновлять.14 октября 2016 г. я создал пакет, совместимый с maven/gradle. https://github.com/jneat/jneat и объяснил это здесь http://rumatoest.blogspot.com/2015/06/synchronize-code-over-multiple-jvm.html

Вы можете использовать реестр, хотя это нерешительно противоречит цели использования языка высокого уровня, такого как Java.По крайней мере, ваша целевая платформа — Windows =D

Попробуйте JUnique:

String appId = "com.example.win.run.main";
boolean alreadyRunning;
try {
    JUnique.acquireLock(appId);
    alreadyRunning = false;
} catch (AlreadyLockedException e) {
    alreadyRunning = true;
}
if (alreadyRunning) {
    Sysout("An Instance of this app is already running");
    System.exit(1);
}

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

Итак, вот что я сделал:

import java.io.File;
import java.io.IOException;

/**
 * This static class is in charge of file-locking the program
 * so no more than one instance can be run at the same time.
 * @author nirei
 */
public class SingleInstanceLock {

    private static final String LOCK_FILEPATH = System.getProperty("java.io.tmpdir") + File.separator + "lector.lock";
    private static final File lock = new File(LOCK_FILEPATH);
    private static boolean locked = false;

    private SingleInstanceLock() {}

    /**
     * Creates the lock file if it's not present and requests its deletion on
     * program termination or informs that the program is already running if
     * that's the case.
     * @return true - if the operation was succesful or if the program already has the lock.<br>
     * false - if the program is already running
     * @throws IOException if the lock file cannot be created.
     */
    public static boolean lock() throws IOException {
        if(locked) return true;

        if(lock.exists()) return false;

        lock.createNewFile();
        lock.deleteOnExit();
        locked = true;
        return true;
    }
}

Использование System.getProperty("java.io.tmpdir") для пути к файлу блокировки гарантирует, что вы всегда будете создавать блокировку в одном и том же месте.

Затем из вашей программы вы просто вызываете что-то вроде:

blah blah main(blah blah blah) {
    try() {
        if(!SingleInstanceLock.lock()) {
            System.out.println("The program is already running");
            System.exit(0);
        }
    } catch (IOException e) {
        System.err.println("Couldn't create lock file or w/e");
        System.exit(1);
    }
}

И это меня устраивает.Теперь, если вы закроете программу, она не удалит файл блокировки, но вы можете решить эту проблему, записав PID программы в файл блокировки и выполнив метод lock(), проверяющий, запущен ли этот процесс уже.Это оставлено в качестве оценки для всех, кому интересно.:)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top