Не перезапускайте мое приложение, если оно уже открыто [дубликат]

Получение фактов _ и __ довольно легко; другие ответы выражают их довольно хорошо. Использование намного сложнее определить.

Вот как я это вижу:

_

Следует использовать, чтобы указать, что функция не предназначена для публичного использования, например, для API. Это и ограничение импорта заставляют его вести себя так же, как internal в c #.

__

Следует использовать, чтобы избежать столкновения имен в иерархии inheritace и избежать latebinding.

==>

Если вы хотите указать, что что-то не для общего пользования, но оно должно действовать как protected использовать _. Если вы хотите указать, что что-то не для общего пользования, но оно должно действовать как private use __.

Это также цитата, которая мне очень нравится:

Проблема заключается в том, что автор класса может законно думать, что «это имя атрибута / метода должно быть закрытым, доступным только из этого определения класса» и использовать соглашение __private. Но в дальнейшем пользователь этого класса может сделать подкласс, который законно нуждается в доступе к этому имени. Поэтому либо надкласс должен быть изменен (что может быть сложно или невозможно), либо код подкласса должен использовать вручную искаженные имена (что в лучшем случае является уродливым и хрупким).

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

83
задан Andrew Thompson 24 June 2012 в 18:59
поделиться

15 ответов

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

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

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

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

удивительно, что getLocalHost возвращает 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

57
ответ дан Community 21 August 2018 в 08:09
поделиться
  • 1
    Имейте в виду, что решение кулака имеет ошибку. Недавно мы обнаружили, что InetAddress.getLocalHost() работает не так, как ожидалось, в DHCP-среде, потому что возвращаемый адрес зависит от того, имеет ли компьютер доступ к сети. Решение заключалось в том, чтобы открыть соединение с InetAddress.getByAddress(new byte[] {127, 0, 0, 1});. – Ahe 17 September 2010 в 09:31
  • 2
    @Ahe: отличная точка. Я включил ваш комментарий, а также ссылки на отчеты Oracle-Sun в моем отредактированном ответе. – VonC 17 September 2010 в 09:41
  • 3
  • 4
  • 5
    @Puce Конечно, никаких проблем: я восстановил эти ссылки. – VonC 23 September 2015 в 17:46

Я использовал для этого сокеты и в зависимости от того, является ли приложение на стороне клиента или на стороне сервера, поведение немного отличается:

  • клиентская сторона: если экземпляр уже существует (я не могу слушать на конкретном порту) Я передам параметры приложения и выйду (вы можете выполнить некоторые действия в предыдущем экземпляре), если я не запустил приложение.
  • серверная сторона: если экземпляр уже существует, я будет печатать сообщение и выходить, если нет, я запустил приложение.
1
ответ дан adrian.tarau 21 August 2018 в 08:09
поделиться

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

9
ответ дан Andrew Thompson 21 August 2018 в 08:09
поделиться
  • 1
    Также обратите внимание, что кажется, что исполняемый экземпляр может быть проинформирован о новых экземплярах и их аргументах, что упрощает общение с такой программой. – Thorbjørn Ravn Andersen 26 November 2012 в 13:17

Вы можете открыть файл с отображением памяти, а затем посмотреть, открыт ли этот файл. Если он уже открыт, вы можете вернуться с основного.

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

В противном случае вы можете открыть сокет в режиме прослушивания (ServerSocket). Сначала попробуйте подключиться к hte socket; если вы не можете подключиться, откройте сервер. если вы подключаетесь, то вы знаете, что еще один экземпляр уже запущен.

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

BR, ~ A

1
ответ дан anjanb 21 August 2018 в 08:09
поделиться
  • 1
    у вас есть код для любой из этих идей? также, что, если я хочу, чтобы, если пользователь запускает новый экземпляр, он закроет все предыдущие? – android developer 2 October 2013 в 13:15

РЕДАКТ. Вместо использования этого подхода WatchService простой 1-секундный поток таймера можно использовать, чтобы проверить, является ли индикаторFile.exists (). Удалите его, а затем принесите приложение в Front ().

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

Просто скачайте Microsoft Windows Sysinternals TCPView (или используйте netstat), запустите его, сортируйте по «Состояние », найдите строковый блок, который говорит« LISTENING », выберите тот, чей удаленный адрес говорит имя вашего компьютера, поместите этот порт в ваше решение new-Socket (). В моей реализации я могу каждый раз производить сбой. И это логический , потому что это и есть основа подхода. Или, что я не получаю относительно того, как это реализовать?

Пожалуйста, сообщите мне, если и как я ошибаюсь в этом!

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

Недостатки из подхода сокета в сравнении:

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

У меня просто была хорошая идея, как для решения проблемы связи Java с новым экземпляром и существующим экземпляром таким образом, который должен работать на каждой системе. Итак, я взбесил этот класс примерно через два часа. Работает как шарм: D

Он основан на Роберте подходом к блокировке файлов (также на этой странице), который я использовал с тех пор. Чтобы сообщить уже запущенному экземпляру, что другой экземпляр попытался запустить (но не сделал) ... файл создается и сразу же удаляется, а первый экземпляр использует WatchService для обнаружения изменения содержимого этой папки. Я не могу поверить, что, по-видимому, это новая идея, учитывая, насколько фундаментальной является проблема.

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

Пример использования:

public static void main(final String[] args) {

    // ENSURE SINGLE INSTANCE
    if (!SingleInstanceChecker.INSTANCE.isOnlyInstance(Main::otherInstanceTriedToLaunch, false)) {
        System.exit(0);
    }

    // launch rest of application here
    System.out.println("Application starts properly because it's the only instance.");
}

private static void otherInstanceTriedToLaunch() {
    // Restore your application window and bring it to front.
    // But make sure your situation is apt: This method could be called at *any* time.
    System.err.println("Deiconified because other instance tried to start.");
}

Вот класс:

package yourpackagehere;

import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.nio.file.*;




/**
 * SingleInstanceChecker v[(2), 2016-04-22 08:00 UTC] by dreamspace-president.com
 * <p>
 * (file lock single instance solution by Robert https://stackoverflow.com/a/2002948/3500521)
 */
public enum SingleInstanceChecker {

    INSTANCE; // HAHA! The CONFUSION!


    final public static int POLLINTERVAL = 1000;
    final public static File LOCKFILE = new File("SINGLE_INSTANCE_LOCKFILE");
    final public static File DETECTFILE = new File("EXTRA_INSTANCE_DETECTFILE");


    private boolean hasBeenUsedAlready = false;


    private WatchService watchService = null;
    private RandomAccessFile randomAccessFileForLock = null;
    private FileLock fileLock = null;


    /**
     * CAN ONLY BE CALLED ONCE.
     * <p>
     * Assumes that the program will close if FALSE is returned: The other-instance-tries-to-launch listener is not
     * installed in that case.
     * <p>
     * Checks if another instance is already running (temp file lock / shutdownhook). Depending on the accessibility of
     * the temp file the return value will be true or false. This approach even works even if the virtual machine
     * process gets killed. On the next run, the program can even detect if it has shut down irregularly, because then
     * the file will still exist. (Thanks to Robert https://stackoverflow.com/a/2002948/3500521 for that solution!)
     * <p>
     * Additionally, the method checks if another instance tries to start. In a crappy way, because as awesome as Java
     * is, it lacks some fundamental features. Don't worry, it has only been 25 years, it'll sure come eventually.
     *
     * @param codeToRunIfOtherInstanceTriesToStart Can be null. If not null and another instance tries to start (which
     *                                             changes the detect-file), the code will be executed. Could be used to
     *                                             bring the current (=old=only) instance to front. If null, then the
     *                                             watcher will not be installed at all, nor will the trigger file be
     *                                             created. (Null means that you just don't want to make use of this
     *                                             half of the class' purpose, but then you would be better advised to
     *                                             just use the 24 line method by Robert.)
     *                                             <p>
     *                                             BE CAREFUL with the code: It will potentially be called until the
     *                                             very last moment of the program's existence, so if you e.g. have a
     *                                             shutdown procedure or a window that would be brought to front, check
     *                                             if the procedure has not been triggered yet or if the window still
     *                                             exists / hasn't been disposed of yet. Or edit this class to be more
     *                                             comfortable. This would e.g. allow you to remove some crappy
     *                                             comments. Attribution would be nice, though.
     * @param executeOnAWTEventDispatchThread      Convenience function. If false, the code will just be executed. If
     *                                             true, it will be detected if we're currently on that thread. If so,
     *                                             the code will just be executed. If not so, the code will be run via
     *                                             SwingUtilities.invokeLater().
     * @return if this is the only instance
     */
    public boolean isOnlyInstance(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {

        if (hasBeenUsedAlready) {
            throw new IllegalStateException("This class/method can only be used once, which kinda makes sense if you think about it.");
        }
        hasBeenUsedAlready = true;

        final boolean ret = canLockFileBeCreatedAndLocked();

        if (codeToRunIfOtherInstanceTriesToStart != null) {
            if (ret) {
                // Only if this is the only instance, it makes sense to install a watcher for additional instances.
                installOtherInstanceLaunchAttemptWatcher(codeToRunIfOtherInstanceTriesToStart, executeOnAWTEventDispatchThread);
            } else {
                // Only if this is NOT the only instance, it makes sense to create&delete the trigger file that will effect notification of the other instance.
                //
                // Regarding "codeToRunIfOtherInstanceTriesToStart != null":
                // While creation/deletion of the file concerns THE OTHER instance of the program,
                // making it dependent on the call made in THIS instance makes sense
                // because the code executed is probably the same.
                createAndDeleteOtherInstanceWatcherTriggerFile();
            }
        }

        optionallyInstallShutdownHookThatCleansEverythingUp();

        return ret;
    }


    private void createAndDeleteOtherInstanceWatcherTriggerFile() {

        try {
            final RandomAccessFile randomAccessFileForDetection = new RandomAccessFile(DETECTFILE, "rw");
            randomAccessFileForDetection.close();
            Files.deleteIfExists(DETECTFILE.toPath()); // File is created and then instantly deleted. Not a problem for the WatchService :)
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private boolean canLockFileBeCreatedAndLocked() {

        try {
            randomAccessFileForLock = new RandomAccessFile(LOCKFILE, "rw");
            fileLock = randomAccessFileForLock.getChannel().tryLock();
            return fileLock != null;
        } catch (Exception e) {
            return false;
        }
    }


    private void installOtherInstanceLaunchAttemptWatcher(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {

        // PREPARE WATCHSERVICE AND STUFF
        try {
            watchService = FileSystems.getDefault().newWatchService();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        final File appFolder = new File("").getAbsoluteFile(); // points to current folder
        final Path appFolderWatchable = appFolder.toPath();


        // REGISTER CURRENT FOLDER FOR WATCHING FOR FILE DELETIONS
        try {
            appFolderWatchable.register(watchService, StandardWatchEventKinds.ENTRY_DELETE);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }


        // INSTALL WATCHER THAT LOOKS IF OUR detectFile SHOWS UP IN THE DIRECTORY CHANGES. IF THERE'S A CHANGE, ANOTHER INSTANCE TRIED TO START, SO NOTIFY THE CURRENT ONE OF THAT.
        final Thread t = new Thread(() -> watchForDirectoryChangesOnExtraThread(codeToRunIfOtherInstanceTriesToStart, executeOnAWTEventDispatchThread));
        t.setDaemon(true);
        t.setName("directory content change watcher");
        t.start();
    }


    private void optionallyInstallShutdownHookThatCleansEverythingUp() {

        if (fileLock == null && randomAccessFileForLock == null && watchService == null) {
            return;
        }

        final Thread shutdownHookThread = new Thread(() -> {
            try {
                if (fileLock != null) {
                    fileLock.release();
                }
                if (randomAccessFileForLock != null) {
                    randomAccessFileForLock.close();
                }
                Files.deleteIfExists(LOCKFILE.toPath());
            } catch (Exception ignore) {
            }
            if (watchService != null) {
                try {
                    watchService.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        Runtime.getRuntime().addShutdownHook(shutdownHookThread);
    }


    private void watchForDirectoryChangesOnExtraThread(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {

        while (true) { // To eternity and beyond! Until the universe shuts down. (Should be a volatile boolean, but this class only has absolutely required features.)

            try {
                Thread.sleep(POLLINTERVAL);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            final WatchKey wk;
            try {
                wk = watchService.poll();
            } catch (ClosedWatchServiceException e) {
                // This situation would be normal if the watcher has been closed, but our application never does that.
                e.printStackTrace();
                return;
            }

            if (wk == null || !wk.isValid()) {
                continue;
            }


            for (WatchEvent<?> we : wk.pollEvents()) {

                final WatchEvent.Kind<?> kind = we.kind();
                if (kind == StandardWatchEventKinds.OVERFLOW) {
                    System.err.println("OVERFLOW of directory change events!");
                    continue;
                }


                final WatchEvent<Path> watchEvent = (WatchEvent<Path>) we;
                final File file = watchEvent.context().toFile();


                if (file.equals(DETECTFILE)) {

                    if (!executeOnAWTEventDispatchThread || SwingUtilities.isEventDispatchThread()) {
                        codeToRunIfOtherInstanceTriesToStart.run();
                    } else {
                        SwingUtilities.invokeLater(codeToRunIfOtherInstanceTriesToStart);
                    }

                    break;

                } else {
                    System.err.println("THIS IS THE FILE THAT WAS DELETED: " + file);
                }

            }

            wk.reset();
        }
    }

}
0
ответ дан Dreamspace President 21 August 2018 в 08:09
поделиться
  • 1
    Для решения этой проблемы вам не нужны сотни строк кода. new ServerSocket() с блоком catch достаточно адекватен, – user207421 22 April 2016 в 08:30
  • 2
    @EJP Вы имеете в виду принятый ответ или о чем вы говорите? Я искал довольно много для решения безплатной библиотеки x-платформы, которое не подведет, например. потому что какой-то сокет уже занял приложение different . Если есть решение для этого - особенно как супер просто, как вы говорите, тогда я хотел бы узнать об этом. – Dreamspace President 22 April 2016 в 12:45
  • 3
    @EJP: Я хочу снова спросить 1) , какое тривиальное решение вы имеете в виду, что вы болтались, как морковь перед моей головой, 2) в случае, если это решение сокета, принятый ответ начинается с того, если один или несколько из моих «Недостатков подхода сокета» & quot; а также 3) , если да, то почему, несмотря на эти недостатки, вы по-прежнему рекомендуете использовать такой подход, как мой. – Dreamspace President 26 April 2016 в 09:26
  • 4
    @EJP: Проблема в том, что ваш голос имеет довольно большой вес, как вы знаете, но все доказательства того, что я заставляет меня убедить, что ваш совет здесь неверен. Понимаете, я не настаиваю на том, что мое решение правильное и все такое, но я - доказательная машина. Разве вы не видите, что ваша должность дает вам ответственность для сообщества, чтобы заполнить недостающие части головоломки этого сообщения, которые начали you ? – Dreamspace President 26 April 2016 в 09:34
  • 5
    @EJP: Поскольку от вас, к сожалению, не было никакой реакции, вот что я буду считать фактом: правда о решении серверного сокета заключается в том, что он действительно глубоко ошибочен , и причина для большинства, кто выбрал возможно, это было «Другие используют это тоже». Или они могли быть обмануты использованием его безответственными людьми. I Угадай , что часть причины, по которой вы не оправдали нас необходимыми объяснениями, заключается в том, что вы не можете понять, почему / почему вы никогда не сомневались в этом подходе, и вы не хотите публичное заявление, раскрывающее это. – Dreamspace President 4 May 2016 в 12:45

Класс ManagementFactory поддерживается в J2SE 5.0 или новее detail

, но теперь я использую J2SE 1.4, и я нашел этот http://audiprimadhanty.wordpress.com/ 2008/06/30 / security-one-instance-of-application-running-at-one-time / , но я никогда не тестирую. Что вы думаете об этом?

2
ответ дан Fuangwith S. 21 August 2018 в 08:09
поделиться

public class SingleInstance {
    public static final String LOCK = System.getProperty("user.home") + File.separator + "test.lock";
    public static final String PIPE = System.getProperty("user.home") + File.separator + "test.pipe";
    private static JFrame frame = null;

    public static void main(String[] args) {
        try {
            FileChannel lockChannel = new RandomAccessFile(LOCK, "rw").getChannel();
            FileLock flk = null; 
            try {
                flk = lockChannel.tryLock();
            } catch(Throwable t) {
                t.printStackTrace();
            }
            if (flk == null || !flk.isValid()) {
                System.out.println("alread running, leaving a message to pipe and quitting...");
                FileChannel pipeChannel = null;
                try {
                    pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel();
                    MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1);
                    bb.put(0, (byte)1);
                    bb.force();
                } catch (Throwable t) {
                    t.printStackTrace();
                } finally {
                    if (pipeChannel != null) {
                        try {
                            pipeChannel.close();
                        } catch (Throwable t) {
                            t.printStackTrace();
                        }
                    } 
                }
                System.exit(0);
            }
            //We do not release the lock and close the channel here, 
            //  which will be done after the application crashes or closes normally. 
            SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                        createAndShowGUI();
                    }
                }
            );

            FileChannel pipeChannel = null;
            try {
                pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel();
                MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1);
                while (true) {
                    byte b = bb.get(0);
                    if (b > 0) {
                        bb.put(0, (byte)0);
                        bb.force();
                        SwingUtilities.invokeLater(
                            new Runnable() {
                                public void run() {
                                    frame.setExtendedState(JFrame.NORMAL);
                                    frame.setAlwaysOnTop(true);
                                    frame.toFront();
                                    frame.setAlwaysOnTop(false);
                                }
                            }
                        );
                    }
                    Thread.sleep(1000);
                }
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                if (pipeChannel != null) {
                    try {
                        pipeChannel.close();
                    } catch (Throwable t) {
                        t.printStackTrace();
                    } 
                } 
            }
        } catch(Throwable t) {
            t.printStackTrace();
        } 
    }

    public static void createAndShowGUI() {

        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(800, 650);
        frame.getContentPane().add(new JLabel("MAIN WINDOW", 
                    SwingConstants.CENTER), BorderLayout.CENTER);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

1
ответ дан George 21 August 2018 в 08:09
поделиться

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

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

Я думаю, что это поможет тем, у кого установлена ​​строгая настройка брандмауэра.

5
ответ дан Ikon 21 August 2018 в 08:09
поделиться
  • 1
    Да, это хороший способ, поскольку блокировка будет выпущена, если приложение разбилось или так :) – LE GALL Benoît 18 June 2015 в 14:45

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

4
ответ дан Jacek Szymański 21 August 2018 в 08:09
поделиться

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

2
ответ дан Javamann 21 August 2018 в 08:09
поделиться
  • 1
    Мне нравится эта идея, так как API прост, но, возможно, некоторые антивирусные сканеры не хотели бы, чтобы вы меняли реестр, поэтому у вас возникают аналогичные проблемы, как с использованием RMI на системах с программным брандмауэром .... не уверен. – Cal 1 November 2009 в 05:43
  • 2
    @Cal Но та же проблема связана с изменением / блокировкой файлов и т. Д. ... не так ли? – Alex 1 October 2013 в 12:26

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

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

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

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

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

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

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

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

  • При запуске приложения каждый экземпляр пытается присоединиться к именованной группе, например «Моя отличная группа приложений». Вы настроили эту группу, чтобы разрешить 0, 1 или N членов
  • Когда количество членов группы больше, чем вы настроили для него. Ваше приложение должно отказаться от запуска.
2
ответ дан johnm 21 August 2018 в 08:09
поделиться

Да, это действительно достойный ответ для приложения eclipse RCP eclipse для одного экземпляра ниже, это мой код

в application.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;
}
7
ответ дан Josh Kelley 21 August 2018 в 08:09
поделиться

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

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

  • При запуске попробуйте открыть прослушиватель на порту XXXX на localhost
  • , если он не работает, откройте сценарий для этого порта на локальном хосте и отправьте аргументы командной строки, затем выключите
  • , в противном случае выслушайте порт XXXXX на локальном хосте. При получении команд командной строки обрабатывайте их так, как если бы приложение запускалось с этой командной строкой.
5
ответ дан Kevin Day 21 August 2018 в 08:09
поделиться

Вы можете использовать библиотеку 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
ответ дан kolobok 21 August 2018 в 08:09
поделиться
  • 1
    Могу ли я использовать это, чтобы предотвратить появление нескольких экземпляров Java-приложения в сети? aka, только один экземпляр моего приложения разрешен во всей моей сети – Wuaner 25 April 2017 в 02:29

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

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;
}
53
ответ дан Robert 21 August 2018 в 08:09
поделиться
  • 1
    С какими операционными системами вы это подтвердили? – Thorbjørn Ravn Andersen 26 November 2012 в 13:14
  • 2
    Я могу подтвердить это на платформах Windows 7/8. – resilva87 20 April 2014 в 00:27
  • 3
    Действительно ли необходимо вручную отменить блокировку файла и закрыть файл при завершении работы? Разве это не происходит автоматически, когда процесс умирает? – Natix 7 October 2014 в 15:50
  • 4
    Но что произойдет, если власть умрет, а компьютер выключится, не запустив крюк отключения? Файл будет сохранен, и приложение будет недоступно. – Petr Hudeček 13 December 2014 в 19:28
  • 5
    @ ПетрХудечек. Все в порядке. Независимо от того, как приложение закончится, блокировка файла будет выпущена. Если это не было надлежащее завершение работы, то это даже имеет преимущество, позволяющее приложению реализовать это при следующем запуске. В любом случае: блокировка - это то, что считается, а не наличие самого файла. Если файл все еще существует, приложение все равно будет запускаться. – Dreamspace President 21 December 2015 в 13:23
Другие вопросы по тегам:

Похожие вопросы: