仅在尚未运行的情况下启动 Java 程序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/660205/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 17:31:26  来源:igfitidea点击:

Start Java program only if not already running

javamutual-exclusion

提问by Morinar

I need to start 1-3 external programs in my Java application that have paths defined by the user. I have few requirements:

我需要在我的 Java 应用程序中启动 1-3 个外部程序,这些程序具有用户定义的路径。我有几个要求:

  1. I don't want the program to execute if it is already running

  2. I don't want any of the programs to steal focus from my Java application

  3. I don't care if any of them fail to start or not. They just need to fail silently.

  1. 如果程序已经在运行,我不希望它执行

  2. 我不希望任何程序从我的 Java 应用程序中窃取焦点

  3. 我不在乎他们中的任何一个是否无法启动。他们只需要默默地失败。

Here is what I have come up with so far:

这是我到目前为止的想法:

ProcessBuilder pb = new ProcessBuilder(userDefinedPath1);
try {
    pb.start();
}
catch (Exception e) {
    // Something went wrong, just ignore
}

And then I repeat that 3 more times with the other two paths. This starts like I would expect and meets my third requirement just fine, but fails on the first two.

然后我用其他两条路径再重复 3 次。这就像我期望的那样开始并且很好地满足了我的第三个要求,但在前两个上失败了。

What is the best way to do this?

做这个的最好方式是什么?

Edit:

编辑:

  1. I don't have any control of these other apps. They are third party. Also, they could have been start or stopped by the user manually at any time.

  2. I know the exactnames of the executables (e.g. "blah.exe") and they will alwaysbe the same, but the paths to the executables won't necessarily be.

  3. Batch file wrappers are not feasible here.

  4. The other apps are notjava apps, just plain old Windows executables.

  1. 我对这些其他应用程序没有任何控制权。他们是第三方。此外,它们可以随时由用户手动启动或停止。

  2. 我知道可执行文件的确切名称(例如“blah.exe”),它们将始终相同,但可执行文件的路径不一定相同。

  3. 批处理文件包装器在这里不可行。

  4. 其他应用程序不是java 应用程序,只是普通的旧 Windows 可执行文件。

采纳答案by Bill K

I'm guessing you don't have control over the other two apps... If you did, this wouldn't be too bad--you could just have them listen to a socket and see if the socket is available when you come up.

我猜你无法控制其他两个应用程序......如果你控制了,这也不会太糟糕——你可以让他们监听一个套接字,看看你来的时候这个套接字是否可用向上。

The next solution may actually be language independent. You could manage the whole system through batch file wrappers. Write a batch file that creates a file when it starts up and deletes it when it stops. Unix systems use this technique a lot--they call the file a lock file more often than not.

下一个解决方案实际上可能与语言无关。您可以通过批处理文件包装器管理整个系统。编写一个批处理文件,它在启动时创建一个文件,并在它停止时删除它。Unix 系统经常使用这种技术——它们经常将文件称为锁定文件。

If only your app will ever start these other apps, then you could simply track if you've started it or not, so I'm guessing this isn't possible or you wouldn't be asking, so I'm assuming that the user may have launched these programs through some other mechanism.

如果您的应用程序会启动这些其他应用程序,那么您可以简单地跟踪是否已启动它,所以我猜这是不可能的,或者您不会问,所以我假设用户可能通过某种其他机制启动了这些程序。

If you have NO control over the launching of the other apps and can't even write a batch file to launch them, then you just can't do what you want to do (Note, the apps would have to always use the batch file, even if the user started them by hand).

如果您无法控制其他应用程序的启动,甚至无法编写批处理文件来启动它们,那么您就无法做您想做的事情(注意,应用程序必须始终使用批处理文件,即使用户手动启动它们)。

I just a very-last ditch effort might be to get a process status and parse it, but you'd have to know exactly what the other apps were called in the PS, this isn't really trivial. Also, all java apps tend to have the same exact signature in most process status printouts which could make this useless.

我只是最后的努力可能是获取进程状态并对其进行解析,但是您必须确切知道 PS 中其他应用程序的名称,这并非微不足道。此外,所有 Java 应用程序在大多数进程状态打印输出中都倾向于具有完全相同的签名,这可能会使其无用。

The problem is that if one of these programs were started outside your app, you have virtually NO WAY to identify that fact unless you happen to know it's exact process status signature, and even then it's flaky.

问题是,如果这些程序之一是在您的应用程序之外启动的,您几乎无法识别该事实,除非您碰巧知道它是确切的进程状态签名,即使这样它也是不稳定的。

回答by Daniel Nesbitt

If I were dealing with this problem on a unix based system I would be tempted to write a native function to gather process information and attempt to launch the external applications. This goes against the spirit of Java, but the type of information you are trying to gather and the control of application launch are outside of the JVM.

如果我在基于 unix 的系统上处理这个问题,我会很想编写一个本机函数来收集进程信息并尝试启动外部应用程序。这违背了 Java 的精神,但是您尝试收集的信息类型和应用程序启动的控制在 JVM 之外。

I do not know enough about Windows programming to point you in the right direction, but I imagine there are Windows API calls you could access in .NET languages or plain C++ that could help you with your second criterion. Perhaps you could change the question to attract the non-Java developers who could help.

我对 Windows 编程知之甚少,无法为您指明正确的方向,但我想您可以使用 .NET 语言或纯 C++ 访问 Windows API 调用,这可以帮助您满足第二个标准。也许您可以更改问题以吸引可以提供帮助的非 Java 开发人员。



Edit:

编辑:

Check out the Shell Execute Functionof the Windows API. The SW_SHOWNOACTIVATE option appears to allow the current window to remain active.

查看Windows API的Shell 执行函数。SW_SHOWNOACTIVATE 选项似乎允许当前窗口保持活动状态。

回答by RealHowTo

See Check if a program or process is running (Windows)

请参阅检查程序或进程是否正在运行 (Windows)

This tip will detect if a given program is running. Ok, it's not pretty because of the vbscript but it's easy to implement.

此提示将检测给定程序是否正在运行。好吧,它不是很漂亮,因为 vbscript 但它很容易实现。

回答by Adrian Pascalin

on windows xp (pro?) you can launch the command 'tasklist' and parse the output to determine if a process is running. you can use threads for avoiding any problem with focus (i think)

在 windows xp (pro?) 上,您可以启动命令“tasklist”并解析输出以确定进程是否正在运行。您可以使用线程来避免任何焦点问题(我认为)

回答by Adrian Pascalin

You can use jps to know the running java program status. jps is one the java tool like javac, java etc.,

您可以使用 jps 来了解正在运行的 java 程序状态。jps 是一种 java 工具,如 javac、java 等,

回答by Michael Rentmeister

This is what I did to solve the problem, it's a simple solution in for using pure java:

这就是我为解决问题所做的,这是使用纯 Java 的简单解决方案:

Note, if the following code crashes once, it is left in an inconsistent state and has no way of dealing with a hung or crashed program.

请注意,如果以下代码崩溃一次,它会处于不一致状态,并且无法处理挂起或崩溃的程序。

public void main(String[] args){
    if(isRunning()){
        JOptionPane.showMessageDialog(this, "2 instances of this program cannot be running at the same time. \n Exiting now");
        System.exit(0);
    }else{
        onStart();
    }
}    

public final void onStart(){
    Preferences prefs;
    prefs = Preferences.userRoot().node(this.getClass().getName());
    prefs.put("RUNNING", "true");
}

public final void onFinish(){
    Preferences prefs;
    prefs = Preferences.userRoot().node(this.getClass().getName());
    prefs.put("RUNNING", "false");
}

public boolean isRunning(){
    Preferences prefs;
    prefs = Preferences.userRoot().node(this.getClass().getName());
    return prefs.get("RUNNING", null) != null ? Boolean.valueOf(prefs.get("RUNNING", null)) : false;
}

private void formWindowClosing(java.awt.event.WindowEvent evt) {
    onFinish();
}

回答by Eric Leschinski

I provide two answers, one for Linux:

我提供了两个答案,一个针对 Linux:

Don't run the program if it's already running, put this in a file called Main.java

如果程序已经在运行,不要运行它,把它放在一个名为 Main.java 的文件中

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

class JustOneLock {
  FileLock lock;
  FileChannel channel;

  public boolean isAppActive() throws Exception{
    File file = new File(System.getProperty("user.home"),
            "FireZeMissiles1111" + ".tmp");
    channel = new RandomAccessFile(file, "rw").getChannel();

    lock = channel.tryLock();
    if (lock == null) {
      return true;
    }
    Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
        try {
          lock.release();
          channel.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
    return false;
  }
}

public class Main {
  public static void main(String[] args)throws Exception {
    JustOneLock u = new JustOneLock();

    if (u.isAppActive()) {
      System.out.println("Already active, stop!");
      System.exit(1);
    }
    else {
      System.out.println("NOT active... Do hard work for 5 seconds.");
      try{Thread.sleep(5000);}catch(Exception e){}
    }
  }
}

Compile it and run it. Then open a new terminal and try to run it again while the other is running and it won't.

编译并运行它。然后打开一个新终端并尝试在另一个终端运行时再次运行它,但它不会。

Another answer for Windows

Windows 的另一个答案

This program will not allow itself to be run if it is already running on the current system. This is for windows only systems.

如果该程序已在当前系统上运行,则该程序将不允许其自身运行。这仅适用于 Windows 系统。

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(5000);
        } 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 is designed to get the PID from the windows system, it may
        //not work for Linux or Mac.  You'll have to acquire a suitable getCurrentPID function
        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
        //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 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");
        }
    }
}

The strategy of the above code is to keep the PID around from the last run, if that PID is found running on the system, don't start. If you finish, reset.

上面代码的策略是保留上次运行的PID,如果发现该PID在系统上运行,则不要启动。如果完成,请重置。

The preferences are stored on Windows Registry in HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs

首选项存储在 Windows 注册表中 HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs

I advise against using File Locking to make sure a java application doesn't run twice at the same time because if the program crashes or hangs forever and is killed, then the lock is left in an inconsistent state and perhaps may even survive a reboot which will cause problems because how will the program know the difference between a program which is still running and a program that has crashed and left the locked file locked?

我建议不要使用文件锁定来确保 Java 应用程序不会同时运行两次,因为如果程序崩溃或永远挂起并被杀死,那么锁将处于不一致的状态,甚至可能在重新启动后仍然存在会导致问题,因为程序如何知道仍在运行的程序和已经崩溃并锁定文件的程序之间的区别?

回答by user2262111

There is one minor thing you can do to all systems, and this includes the file lock ability.

您可以对所有系统做一件小事,这包括文件锁定功能。

On program run First, create a file and lock it. Name it something like lockfile. Generate a timestamp and encrypt it. Write this into it.

在程序运行首先,创建一个文件并锁定它。将其命名为 lockfile 之类的东西。生成时间戳并对其进行加密。把这个写进去。

On same program run First, check if there is a lock file present. Attempt to lock it. Ignore the possibility that this file is locked. Read and check timestamp. If the time stamp is something inanely small (like 1 hour ago), attempt to run the program. You will error writing a timestamp back, for it is locked. In this case, assume there is a program already open.

在同一程序运行首先,检查是否存在锁定文件。尝试锁定它。忽略此文件被锁定的可能性。读取并检查时间戳。如果时间戳非常小(例如 1 小时前),请尝试运行该程序。您将错误写回时间戳,因为它已被锁定。在这种情况下,假设有一个程序已经打开。

On program exit, delete this lock file.

在程序退出时,删除此锁定文件。

回答by nathanfranke

In order to avoid potential lock file / crash issues, it is possible to start a server and catch the port collision. These servers are automatically stopped on system shutdown (Even after a crash)

为了避免潜在的锁定文件/崩溃问题,可以启动服务器并捕获端口冲突。这些服务器在系统关闭时自动停止(即使在崩溃之后)

public static ServerSocket ss;

public static void main (String[] args) {

    ss = null;

    try {
        ss = new ServerSocket(1044);
    } catch (IOException e) {
        System.err.println("Application already running!");
        System.exit(-1);
    }
}