11

我希望我的应用程序检查其自身的另一个版本是否已经在运行。

比如demo.jar启动,用户点击再次运行,但是第二个实例实现“哦等等,已经有一个demo.jar运行了”。并退出并显示一条消息。

4

11 回答 11

11

强制使用 ServerSocket 锁运行的程序的一个实例

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){}
    }
  }
}

编译并运行它

javac Main.java
java Main

在正常情况下测试它:

运行程序。您有 100 秒的时间在另一个终端中再次运行该程序,它会说它已经在运行。然后等待 100 秒,它应该允许您在第二个终端中运行它。

在使用 kill -9 强制停止程序后对其进行测试

  1. 在终端 1 中启动程序。
  2. 在 100 秒内从另一个终端杀死 -9 该进程。
  3. 再次运行程序,就可以运行了。

结论:

当您的程序不再运行时,操作系统会清理套接字占用。所以你可以确定程序不会运行两次。

缺点

如果某个鬼鬼祟祟的人,或者某个顽皮的进程要绑定所有端口,或者只是你的端口,那么你的程序将不会运行,因为它认为它已经在运行。

于 2014-08-14T19:45:58.090 回答
6

您正在寻找的东西可能最好通过锁定文件来完成。通过锁定文件,我只是指一个具有预定义位置并且其存在是您的互斥锁的文件。

在程序启动时测试该文件是否存在,如果存在,立即退出。在已知位置创建文件。如果您的程序正常退出,请删除锁定文件。

最好的办法可能是您还可以使用 pid(进程 ID)填充文件,这样您就可以检测到未删除文件但特定于操作系统的异常退出。

于 2012-05-09T01:52:51.090 回答
5

简单而强大的测试解决方案。

    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 侦听器或其他任何东西......

于 2013-11-16T07:01:59.903 回答
2

如果您的应用程序在 Windows 上运行,您可以通过 JNI调用CreateMutex 。

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。如果您希望所有 Windows 会话共享您的互斥锁,您可以提供“myApplication”作为互斥锁名称或“Global\MyApplication”。

编辑:它并不像看起来那么复杂:) 我觉得它很干净。

于 2012-05-09T02:00:20.067 回答
2

此代码的策略是保留上次运行时在注册表中的 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 将被终止,程序重新运行。

于 2013-08-02T03:36:13.250 回答
2

如果您使用 Mutex,从逻辑上讲,Mutex 需要可以从任何运行“程序”副本的 JVM 访问。在 C 编程中,这可能是通过共享内存来实现的,但 Java 默认没有这样的东西。

有了这种理解,就有很多方法可以实现你想要的。您可以在指定端口上打开服务器套接字(操作系统确保只有一个进程是服务器套接字的接收者,随后的打开失败)。

您可以使用“锁定文件”,但它有点复杂,因为您需要使用的文件实际上是一个目录(并且它在很大程度上取决于目录创建对于您的文件系统是否是原子的,即使大多数目录创建是)。如果系统管理员决定通过 NFS 运行您,那么事情会变得更加困难(如果不是不可能的话)。

您还可以使用 JVM 和调试 / JMI 做一些漂亮的技巧,前提是您可以以某种方式向自己保证所有相关的 JVM 都使用相同的配置启动(及时,这是一项不可能完成的任务)。

其他人已经使用 exec 工具来运行等效的进程列表,但由于可能出现竞争条件(两个进程同时检查,但无法看到对方),这有点棘手。

最后,服务器套接字路由可能是最稳定的,因为它通过 TCP/IP 堆栈保证只绑定到一个进程(并且由操作系统调解)。也就是说,您将不得不刷新传入消息的套接字,这会带来其他安全问题的可能性。

于 2012-05-09T01:59:42.883 回答
1

以下解决方案也在两个致命的场景中工作。1>甚至你启动的exe在任务管理器中安排为javaw.exe。2> 您可以在两个位置安装您的应用程序,并且从启动这两个位置它也可以工作。

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
于 2014-09-11T06:55:46.060 回答
1

与其他几个答案相反,最可靠的方法是ServerSocket在只有您知道的固定端口上创建一个,在油漆卡中向上。与任何锁文件不同,它会在您的应用程序退出时自动释放,并且它之前通过 a 存在BindException是另一个实例已经在运行的非常可靠的标志。

于 2013-11-16T07:20:57.980 回答
1

这是一种使用用户主目录中自动命名的锁定文件的方法。该名称基于运行 jar 的位置。

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_]", "_");
    }
}
于 2016-08-01T16:21:27.497 回答
0

如果您的应用程序可以在具有唯一名称的任务管理器中安排,这也是一个很好的解决方案

 "tasklist /FI \"IMAGENAME eq "+MyApplication+".exe
于 2014-09-11T07:29:44.197 回答
0

检查 PID 和文件锁定技术

我们可以将创建锁文件的进程的进程id写入文件中。当我们遇到一个现有的锁文件时,我们不只是退出,而是检查具有该 id 的进程是否还活着。如果没有,则创建一个新的应用程序实例。我认为 MongoDB 使用这种技术。

    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();
        }
    }
于 2014-09-15T08:31:49.173 回答