Как убедиться, что запущен только один экземпляр приложения Java?

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

Например,demo.jar пользователь щелкает, чтобы запустить его снова, но второй экземпляр понимает "о, подождите, уже естьdemo.jar . Работает & Quot; и выходит с сообщением.

Ответы на вопрос(12)

Check PID and file lock technique

We can write the process id of the process that created the lock file into the file. When we encounter an existing lock file, we do not just quit, but we check if the process with that id is still alive. If not, then create a new application instance. I think MongoDB use this technique.

    static File file;
    static FileChannel fileChannel;
    static FileLock lock;
    static boolean running = false;
    static String currentPID = null;
    static String lockFilePID = null;
    public static final String USER_DIR = System.getProperty("user.dir");
    public static final String LOCK_FILE = "az-client.lock";

    public static boolean checkInstance() {
        try {
            file = new File(USER_DIR + File.separator + LOCK_FILE);
            currentPID = Integer.toString(getCurrentPID());
            if (!file.exists()) {
                file.createNewFile();
                writePID(currentPID);
                lockFile();
                addShudDownHook();
                running = true;
                return running;
            } else {
                if (isFileLocked()) {
                    syso("App already running");
                    System.exit(0);
                } else {
                    lockFilePID = getPIDFromLockFile();
                    if (isProcessIdRunningOnWindows(Integer.parseInt(lockFilePID))) {
                        lockFile();
                        addShudDownHook();
                        running = true;
                        return running;
                    } else {
                        file.delete();
                        file.createNewFile();
                        writePID(currentPID);
                        lockFile();
                        addShudDownHook();
                        running = true;
                        return running;
                    }
                }
            }
        } catch (Exception e) {
            syso(e + "App already running");
            System.exit(0);
        }
        return running;
    }

    /**
     * 
     * @return
     * @throws IOException
     */
    @SuppressWarnings("resource")
    private static boolean isFileLocked() throws IOException {
        fileChannel = new RandomAccessFile(file, "rw").getChannel();
        lock = fileChannel.tryLock();
        if (lock == null) {
            fileChannel.close();
            fileChannel = null;
            return true;
        } else {
            lock.release();
            fileChannel.close();
            fileChannel = null;
        }
        return false;
    }


    public static int getCurrentPID() {
        // This function should work with Windows, Linux and Mac but you'll have
        // to
        // test to make sure. If not then get a suitable getCurrentPID function
        // replacement.
        try {
            java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
            java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
            jvm.setAccessible(true);
            sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
            java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
            pid_method.setAccessible(true);
            return (int) pid_method.invoke(mgmt);
        } catch (Exception e) {
            throw new RuntimeException("Cannot get the current PID");
        }
    }

    public static boolean isProcessIdRunningOnWindows(int pid) {
        // This Function only works for windows, if you want it to work on linux
        // or mac, you will have to go find a replacement method that
        // takes the processID as a parameter and spits out a true/false
        // if it is running on the operating system.
        try {
            Runtime runtime = Runtime.getRuntime();
            String cmds[] = { "cmd", "/c", "tasklist /FI \"PID eq " + pid + "\"" };
            Process proc = runtime.exec(cmds);

            InputStream inputstream = proc.getInputStream();
            InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
            BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
            String line;
            while ((line = bufferedreader.readLine()) != null) {
                if (line.contains(" " + pid + " ")) {
                    return true;
                }
            }
            return false;
        } catch (Exception ex) {
            throw new RuntimeException("Cannot run the tasklist command to query if a pid is running or not");
        }
    }

    /**
     * This method write PID to Lock file
     * 
     * @param pid
     * @throws Exception
     */
    private static void writePID(String pid) throws Exception {
        try {
            // To Do write PID to LockFile
        } catch (Exception e) {
            syso(e);
            throw e;
        }
    }

    /**
     * This method return PID from Lock File
     * 
     * @return
     * @throws Exception
     */
    private static String getPIDFromLockFile() throws Exception {
        try {
            return //To Do getPID from File
        } catch (Exception e) {
            syso(e);
            throw e;
        }
    }

    private static void addShudDownHook() {
        try {
            ShutdownHook shutdownHook = new ShutdownHook();
            Runtime.getRuntime().addShutdownHook(shutdownHook);
        } catch (Exception e) {
            LogWriter.logger.error(e);
        }
    }

    private static void unlockFile() {
        try {
            if (lock != null) {
                lock.release();
            }
            fileChannel.close();
            file.delete();
            running = false;
        } catch (IOException e) {
            syso(e);
        }
    }

    private static void lockFile() {
        try {
            fileChannel = new RandomAccessFile(file, "rw").getChannel();
            lock = fileChannel.tryLock();
            if (lock == null) {
                fileChannel.close();
                fileChannel = null;
            }
        } catch (IOException e) {
            syso(e);
        }
    }

    static class ShutdownHook extends Thread {
        public void run() {
            unlockFile();
        }
    }
Решение Вопроса

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

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

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

 09 мая 2012 г., 07:24
Вы, вероятно, захотите дать пользователю способ удалить файл блокировки на тот случай, если приложение аварийно завершит работу и не удалит его автоматически.
 Epicblood09 мая 2012 г., 04:53
Я не думал об этом, отличная идея, спасибо :)
 Epicblood09 мая 2012 г., 07:42
будет делать, в любом случае, чтобы сделать файл автоматического удаления при перезагрузке?
 09 мая 2012 г., 14:28
@JorisBolsens поместил файл в системный каталог tmp. Я знаю, что это очищает при перезагрузке для Linux, и я думаю, что есть аналогичное расположение для Windows.
 09 мая 2012 г., 04:30
просто дополнение: я не пробовал pid, который безопаснее, если в системе есть PID. Я пробовал это ранее, но обнаружил небольшую проблему (просто ложно-положительный). Теперь это может быть связано с конкретной машиной. Поэтому, когда моя программа закрывается, она удаляет файл. Файл не удалялся сразу (из-за буферизации или чего-то еще), и когда я снова быстро запустил программу, он нашел бы файл (даже если он был удален)

CreateMutex через JNI.

jboolean ret = FALSE;    
HANDLE hMutex = CreateMutex(NULL, FALSE, mutexName); 
ret = TRUE;    
if(WAIT_TIMEOUT == WaitForSingleObject(hMutex, 10))  
{    
    ret = FALSE;  
}
else if(GetLastError() != 0)  
{    
    ret = FALSE;  
}

Возвращает true, если никто не использует этот мьютекс, иначе false. Вы можете предоставить «myApplication» как имя мьютекса или "Global \ MyApplication" если вы хотите, чтобы ваш мьютекс был общим для всех сеансов Windows.

Редактировать: это не так сложно, как кажется :), и я нахожу это чистым.

 Epicblood09 мая 2012 г., 04:51
Я хотел бы иметь возможность использовать на Mac и Linux, но это помогает XD

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

Обратитесь к этому при получении процесса:

http://www.itechp2pexchange.com/content/linux-unix-run-only-one-instance-script

 01 февр. 2013 г., 04:23
Я собрал это от работы над этим ни из какой статьи нигде.
 31 янв. 2013 г., 19:07
Блокировка канала файла Java не работает в Linux? ссылку пожалуйста?
 16 нояб. 2013 г., 08:22
«Любая статья в любом месте»; не является ответом на вопрос и не является каким-либо нормативным справочным или авторитетным источником. Пожалуйста, приведите надежный источник, который говорит об этом, например, принятый отчет об ошибке. Это просто не заслуживает доверия. Упомянутое вами решение связано с серьезной проблемой временного окна. -1
 21 нояб. 2013 г., 05:08
@EJP Также я заметил, что ваш пол отвечает & quot;stackoverflow.com/questions/4175878/…& Quot; упоминается как «Мой отец, доктор медицинских наук, FRACP». Если это более уместно, чем «статья» где-то я немедленно перестану пользоваться компьютером.
 21 нояб. 2013 г., 05:04
Эта ссылка предназначена для запуска одного экземпляра скрипта. Единственный способ - добавить FileLock, а затем запустить два экземпляра со сценарием и без него. Эта статья имеет свою цель в этом смысле. Вы увидите множество других ответов stackoverflow, в которых есть ссылки на другие & quot; статьи & quot; (ваш способ выразить это). На самом деле, это лучше, чем копирование и вставка в вашу собственную работу.

Следующее решение работает в двух смертельных сценариях тоже. 1 & GT; Даже ваш запущенный exe запланирован как javaw.exe в диспетчере задач. 2 & GT; Вы можете установить приложение в двух местах, и после запуска в обоих местах оно также работает.

String tempDir = System.getProperty("java.io.tmpdir");// dependent to OS find any tem dir.
        String filePath = tempDir + "lockReserverd.txt";
        try {
            final File file = new File(filePath);

            if(file.exists())
                return false;

            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");
        }
        return false

или же

Это будет работать, если ваш application.exe указан в диспетчере задач

"tasklist /FI \"IMAGENAME eq "+MyApplication+".exe

Если вы используете Mutex, логично, что Mutex должен был бы быть доступен с любой JVM, на которой выполнялась копия «программы». В программировании на Си это может быть выполнено через разделяемую память, но Java не имеет такой возможности по умолчанию.

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

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

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

Другие люди использовали функцию exec для запуска эквивалента списка процессов g, но это немного сложно из-за возможности состязания (два процесса одновременно проверяют и не видят друг друга).

В конце концов, маршрут сокета сервера, вероятно, является наиболее стабильным, поскольку он гарантированно связывается только с одним процессом стеком TCP / IP (и опосредуется операционной системой). Тем не менее, вам придется очистить сокет входящих сообщений, и это открывает возможность других проблем безопасности.

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

```

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;

public class SingleInstance {

    @SuppressWarnings("resource")
    public static boolean isAlreadyRunning() {
        File file;
        FileChannel fileChannel;
        File userDir = new File(System.getProperty("user.home"));
        file = new File(userDir, myLockName());

        if (!file.exists()) {
            try {
                file.createNewFile();
                file.deleteOnExit();
            } catch (IOException e) {
                throw new RuntimeException("Unable to create Single Instance lock file!", e);
            }
        }

        try {
            fileChannel = new RandomAccessFile(file, "rw").getChannel();
        } catch (FileNotFoundException e) {
            throw new RuntimeException("Single Instance lock file vanished!", e);
        }
        try {
            if (fileChannel.tryLock() != null) {
                return false;
            }
        } catch (Exception e) {
        }
        try {
            fileChannel.close();
        } catch (IOException e1) {
        }
        return true;
    }

    private static String myLockName() {
        return "." + SingleInstance.class.getProtectionDomain().getCodeSource().getLocation().getPath()
                .replaceAll("[^a-zA-Z0-9_]", "_");
    }
}

```

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

Simple yet powerful tested solution.

    static File file;
    static FileChannel fileChannel;
    static FileLock lock;
    static boolean running = false;

    @SuppressWarnings("resource")
    public static boolean checkIfAlreadyRunning() throws IOException {
        file = new File(FilePath.FILEPATH + "az-client.lock");
        if (!file.exists()) {
            file.createNewFile();
            running = true;
        } else {
            file.delete();
        }

        fileChannel = new RandomAccessFile(file, "rw").getChannel();
        lock = fileChannel.tryLock();

        if (lock == null) {
            fileChannel.close();
            return true;
        }
        ShutdownHook shutdownHook = new ShutdownHook();
        Runtime.getRuntime().addShutdownHook(shutdownHook);

        return running;
    }

    public static void unlockFile() {
        try {
            if (lock != null)
                lock.release();
            fileChannel.close();
            file.delete();
            running = false;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static class ShutdownHook extends Thread {
        public void run() {
            unlockFile();
        }
    }

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

 16 нояб. 2013 г., 08:06
Пожалуйста, не забудьте проголосовать +1, если это поможет :-)
 02 сент. 2016 г., 13:48
Просто любопытно - если файл существует (иначе ответвьте от if (! File.exists ()), вы удаляете файл, а затем пытаетесь заблокировать его. Работает ли это нормально, если файл существует и заблокирован? Я немного нахожу логику странно (зачем удалять файл, если он заблокирован?) ...
 11 авг. 2014 г., 11:15
что? Я не понял вопроса
 29 июл. 2014 г., 16:02
Ваш FilePath.FILEPATH не из JDK, у меня нет Jenkins, доступного в моем проекте. Это утверждение, file = new File (FilePath.FILEPATH + "az-client.lock"), должно быть изменено для применения в моем случае. Не могли бы вы сказать мне, как?
Enforce one instance of a program running with a ServerSocket Lock

Java-код Поместите это в файл с именем Main.java:

import java.net.*;
import java.io.*;
public class Main{
  public static void main(String args[]){
    ServerSocket socket = null;
    try {
      socket = new ServerSocket(34567);
      System.out.println("Doing hard work for 100 seconds");
      try{ Thread.sleep(100000); } catch(Exception e){ }
      socket.close();
    }
    catch (IOException ex) {
      System.out.println("App already running, exiting...");
    }
    finally {
      if (socket != null)
          try{ socket.close(); } catch(Exception e){}
    }
  }
}

Compile and run it

javac Main.java
java Main

Test it in a normal case:

Запустите программу. У вас есть 100 секунд, чтобы снова запустить программу в другом терминале, она провалится и скажет, что она уже запущена. Затем подождите 100 секунд, это должно позволить вам запустить его во втором терминале.

Test it after force halting the program with a kill -9

Start the program in terminal 1. kill -9 that process from another terminal within 100 seconds. Run the program again, it is allowed to run.

Conclusion:

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

Drawbacks

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

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

Настройки хранятся в реестре Windows вHKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs

import java.io.*;
import java.util.prefs.Preferences;
public class JavaApplication3 {
    public static void main(String[] args){
        if(isRunning()){
            System.out.println("Two instances of this program cannot " +
                    "be running at the same time.  Exiting now");
        }
        else{
            onStart();
            epicHeavyWorkGoesHere();
            onFinish();
        }
    }
    public static void epicHeavyWorkGoesHere(){
        try {
            Thread.sleep(15000);
        } catch (InterruptedException ex) {}
    }
    public static void onStart(){
        Preferences prefs = Preferences.systemRoot().node("JavaApplication3");
        prefs.put("RUNNINGPID", getCurrentPID());
    }
    public static void onFinish(){
        Preferences prefs = Preferences.systemRoot().node("JavaApplication3");
        prefs.put("RUNNINGPID", "");
    }
    public static boolean isRunning(){
        Preferences prefs = Preferences.systemRoot().node("JavaApplication3");

        if (prefs.get("RUNNINGPID", null) == null || prefs.get("RUNNINGPID", null).equals(""))
            return false;

        if (isProcessIdRunningOnWindows(Integer.parseInt(prefs.get("RUNNINGPID", null))))
            return true;
        return false;
    }
    public static String getCurrentPID(){
        //This function should work with Windows, Linux and Mac but you'll have to 
        //test to make sure.  If not then get a suitable getCurrentPID function replacement.
        try{
            java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
            java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
            jvm.setAccessible(true);
            sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
            java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
            pid_method.setAccessible(true);
            return pid_method.invoke(mgmt) + "";
        }
        catch(Exception e){
            throw new RuntimeException("Cannot get the current PID");
        }
    }
    public static boolean isProcessIdRunningOnWindows(int pid){
        //This Function only works for windows, if you want it to work on linux
        //or mac, you will have to go find a replacement method that 
        //takes the processID as a parameter and spits out a true/false 
        //if it is running on the operating system.
        try {
            Runtime runtime = Runtime.getRuntime();
            String cmds[] = {"cmd", "/c", "tasklist /FI \"PID eq " + pid + "\""};
            Process proc = runtime.exec(cmds);

            InputStream inputstream = proc.getInputStream();
            InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
            BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
            String line;
            while ((line = bufferedreader.readLine()) != null) {
                if (line.contains(" " + pid + " ")){
                    return true;
                }
            }
            return false;
        }
        catch (Exception ex) {
            throw new RuntimeException("Cannot run the tasklist command to query if a pid is running or not");
        }
    }
}

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

 08 авг. 2013 г., 03:04
1) Как это узнать, если программа зависает? Если предыдущая программа зависает, PID в реестре и процесс в списке задач по-прежнему остаются. Вы не могли различить запущенный процесс или зависший процесс. 2) Я мог бы просто просканировать список задач, чтобы найти процесс (может быть запущен или завис). Чего еще можно достичь, сохранив PID в реестре?
 08 авг. 2013 г., 05:20
Ах, хороший улов. Я хотел добавить еще один ключ, который хранит время последнего успешного завершения. Также сохраняйте переменную для "максимального количества времени, отведенного для запуска". Когда программа запускается, время выполнения PID может быть рассчитано и уничтожено, если оно выполняется слишком долго.

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

 "tasklist /FI \"IMAGENAME eq "+MyApplication+".exe

Ваш ответ на вопрос