English 中文(简体)
仅在未运行时才启动Java程序。
原标题:
  • 时间:2009-03-18 21:32:23
  •  标签:

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

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

  2. 我不想让任何程序从我的Java应用程序中夺取焦点。

  3. 我不在乎它们中的任何一个是否启动或未启动。它们只需要静默失败。

这是我迄今为止想出来的。

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

然后我用其他两个路径重复了3次。它开始得很正常,完全满足了我的第三个需求,但在第一和第二个上失败了。

如何做到最好?

编辑:

  1. 我无法控制这些其他的应用程序。它们是第三方的。此外,它们可能随时被用户手动启动或停止。

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

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

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

最佳回答

我猜测您无法控制另外两个应用。如果您能够控制它们,那就好办了 - 您可以让它们监听一个套接字并在启动时检查套接字是否可用。

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

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

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

我认为最后一个尝试的方法可能是获取进程状态并解析它,但你必须确切地知道PS中另一个应用程序的名称,这并不是非常简单的事情。此外,大多数进程状态的打印输出中,所有Java应用程序往往都具有相同的签名,这可能导致此方法无效。

问题在于,如果这些程序中有一个在您的应用程序之外启动,除非您碰巧知道它的确切流程状态签名,否则您几乎无法确定这一事实,而即使您知道,也可能有问题。

问题回答

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

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

I provide two answers, one for Linux:

如果程序已经在运行,请不要再运行它,将此放入一个名为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){}
    }
  }
}

编译并运行它。然后打开一个新的终端,尝试在其他终端正在运行它的同时再次运行它,它将无法运行。

另一个Windows的答案

如果此程序在当前系统中已经在运行,它将不允许自己运行。这仅适用于 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");
        }
    }
}

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

偏好设置存储在Windows注册表中的 HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs 中。

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

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

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

public void main(String[] args){
    if(isRunning()){
        JOptionPane.showMessageDialog(this, "2 instances of this program cannot be running at the same time. 
 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();
}

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

我不太了解Windows编程,无法为您指明正确的方向,但我想在.NET语言或纯C ++中,你可以调用Windows API来帮助您满足第二个标准。也许您可以更改问题,以吸引非Java开发人员来提供帮助。


编辑:

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

你可以对所有系统进行一个小操作,这包括文件锁定功能。

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.

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.

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

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)

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





相关问题
热门标签