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

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

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

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

Ответы [ 9 ]

35 голосов
/ 28 мая 2009

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

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

Кроме того, если я правильно помню, нет простого способа получить 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, что должно исключать любые предупреждения брандмауэра, поскольку любой трафик на этот адрес должен поступать из локальной системы.

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

26 голосов
/ 28 мая 2009

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

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

3 голосов
/ 28 мая 2009

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

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

Пример блокировки файлов приведен, например, в Альманах Java Developers .

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

1 голос
/ 23 ноября 2016

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

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

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

1 голос
/ 20 июля 2012

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

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

Используется метод блокировки файлов.

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

1 голос
/ 28 мая 2009

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

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

0 голосов
/ 12 сентября 2017

Попробуйте 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);
}
0 голосов
/ 06 марта 2014

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

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

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 (), чтобы проверить, запущен ли уже этот процесс. Это оставлено как задание для всех, кто заинтересован. :)

0 голосов
/ 28 мая 2009

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

...