Как реализовать Java-приложение одного экземпляра? - PullRequest
86 голосов
/ 07 октября 2008

Иногда я вижу много приложений, таких как MSN, Windows Media Player и т. Д., Которые являются приложениями с одним экземпляром (когда пользователь запускается во время работы приложения, новый экземпляр приложения не создается).

В C # я использую класс Mutex для этого, но я не знаю, как это сделать в Java.

Ответы [ 15 ]

61 голосов
/ 05 января 2010

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

private static boolean lockInstance(final String lockFile) {
    try {
        final File file = new File(lockFile);
        final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        final FileLock fileLock = randomAccessFile.getChannel().tryLock();
        if (fileLock != null) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    try {
                        fileLock.release();
                        randomAccessFile.close();
                        file.delete();
                    } catch (Exception e) {
                        log.error("Unable to remove lock file: " + lockFile, e);
                    }
                }
            });
            return true;
        }
    } catch (Exception e) {
        log.error("Unable to create and/or lock file: " + lockFile, e);
    }
    return false;
}
59 голосов
/ 07 октября 2008

Если я верю этой статье , по:

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

Примечание: Ahe упоминает в комментарии, что использование InetAddress.getLocalHost() может быть сложным:

  • он не работает должным образом в среде DHCP, поскольку возвращаемый адрес зависит от того, есть ли у компьютера доступ к сети.
    Решением было открыть соединение с InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
    Вероятно, связано с ошибка 4435662 .
  • Я также обнаружил ошибку 4665037 , которая сообщает о ожидаемых результатах getLocalHost: возврат IP-адреса компьютера, в сравнении с фактическими результатами: возврат 127.0.0.1.

Удивительно, что getLocalHost return 127.0.0.1 в Linux, но не в Windows.


Или вы можете использовать ManagementFactory объект. Как объяснено здесь :

Метод getMonitoredVMs(int processPid) получает в качестве параметра PID текущего приложения и отлавливает имя приложения, которое вызывается из командной строки, например, приложение было запущено с пути c:\java\app\test.jar, затем значение переменной равно "c:\\java\\app\\test.jar ». Таким образом, мы поймаем только имя приложения в строке 17 кода ниже.
После этого мы ищем в JVM другой процесс с тем же именем, если мы нашли его, а PID приложения другой, это означает, что это второй экземпляр приложения.

JNLP предлагает также SingleInstanceListener

9 голосов
/ 24 августа 2011

Если приложение. имеет графический интерфейс, запустите его с JWS и используйте SingleInstanceService. См. Демоверсию . SingleInstanceService для примера кода (demo. and).

7 голосов
/ 23 ноября 2010

Да, это действительно достойный ответ для Eclipse RCP. ниже мой код

в приложении. Java

if(!isFileshipAlreadyRunning()){
        MessageDialog.openError(display.getActiveShell(), "Fileship already running", "Another instance of this application is already running.  Exiting.");
        return IApplication.EXIT_OK;
    } 


private static boolean isFileshipAlreadyRunning() {
    // socket concept is shown at http://www.rbgrn.net/content/43-java-single-application-instance
    // but this one is really great
    try {
        final File file = new File("FileshipReserved.txt");
        final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        final FileLock fileLock = randomAccessFile.getChannel().tryLock();
        if (fileLock != null) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    try {
                        fileLock.release();
                        randomAccessFile.close();
                        file.delete();
                    } catch (Exception e) {
                        //log.error("Unable to remove lock file: " + lockFile, e);
                    }
                }
            });
            return true;
        }
    } catch (Exception e) {
       // log.error("Unable to create and/or lock file: " + lockFile, e);
    }
    return false;
}
5 голосов
/ 23 ноября 2016

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

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

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

JUnique реализует блокировки и каналы связи, общие для всех экземпляры JVM, запущенные одним и тем же пользователем.

public static void main(String[] args) {
    String appId = "myapplicationid";
    boolean alreadyRunning;
    try {
        JUnique.acquireLock(appId, new MessageHandler() {
            public String handle(String message) {
                // A brand new argument received! Handle it!
                return null;
            }
        });
        alreadyRunning = false;
    } catch (AlreadyLockedException e) {
        alreadyRunning = true;
    }
    if (!alreadyRunning) {
        // Start sequence here
    } else {
        for (int i = 0; i < args.length; i++) {
            JUnique.sendMessage(appId, args[0]));
        }
    }
}

В этом случае он создает блокировки файлов в папке% USER_DATA% /. Junique и создает сокет сервера на произвольном порту для каждого уникального идентификатора приложения, который позволяет отправлять / получать сообщения между приложениями Java.

5 голосов
/ 16 мая 2009

Я нашел решение, немного мультяшное объяснение, но в большинстве случаев все еще работает. Он использует простой старый материал для создания файла блокировки, но в совершенно ином виде:

http://javalandscape.blogspot.com/2008/07/single-instance-from-your-application.html

Думаю, это поможет тем, у кого строгий брандмауэр.

5 голосов
/ 07 октября 2008

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

Если вы пытаетесь заставить второй экземпляр передать аргументы командной строки и т. Д. ... первому экземпляру, то при использовании соединения через сокет на локальном хосте будет убито две птицы одним камнем. Общий алгоритм:

  • При запуске попробуйте открыть прослушиватель на порту XXXX на localhost
  • если не получится, откройте устройство записи на этот порт на localhost и отправьте аргументы командной строки, затем завершите работу
  • В противном случае прослушивайте порт XXXXX на локальном хосте. При получении аргументов командной строки обработайте их, как если бы приложение было запущено с этой командной строкой.
4 голосов
/ 07 октября 2008

В Windows вы можете использовать launch4j .

2 голосов
/ 22 апреля 2016

Более общий способ ограничения количества экземпляров на одном компьютере или даже во всей сети - использование многоадресного сокета.

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

Таким образом, вы можете включить много типов конфигураций, чтобы контролировать такие вещи, как

  • Один или несколько экземпляров на машину
  • Один или несколько экземпляров на сеть (например, контроль установки на клиентском сайте)

Поддержка многоадресной рассылки Java осуществляется через пакет java.net с MulticastSocket & DatagramSocket , являющимся основными инструментами.

Примечание : MulticastSocket не гарантирует доставку пакетов данных, поэтому вы должны использовать инструмент, построенный поверх сокетов многоадресной рассылки, такой как JGroups . JGroups делает гарантией доставки всех данных. Это один файл jar с очень простым API.

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

Чтобы использовать JGroups, ограничить количество экземпляров приложения (на компьютере или в сети, скажем, количеством лицензий, которые купил клиент) концептуально очень просто:

  • При запуске вашего приложения каждый экземпляр пытается присоединиться к именованной группе, например, «My Great App Group». Вы настроите эту группу, чтобы разрешить 0, 1 или N членов
  • Когда количество членов группы больше, чем настроено для него .. ваше приложение должно отказаться от запуска.
2 голосов
/ 07 октября 2008

Вы можете попробовать использовать API настроек. Это не зависит от платформы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...