Mor*_*nar 15 java mutual-exclusion
我需要在我的Java应用程序中启动1-3个外部程序,这些程序具有用户定义的路径.我的要求很少:
如果程序已经运行,我不希望程序执行
我不希望任何程序从我的Java应用程序中窃取焦点
我不在乎他们中的任何一个是否未能启动.他们只需要默默地失败.
这是我到目前为止提出的:
ProcessBuilder pb = new ProcessBuilder(userDefinedPath1);
try {
pb.start();
}
catch (Exception e) {
// Something went wrong, just ignore
}
Run Code Online (Sandbox Code Playgroud)
然后我用另外两条路径再重复3次.这开始就像我期望的那样,并且满足我的第三个要求就好了,但是前两个失败了.
做这个的最好方式是什么?
编辑:
我对这些其他应用程序没有任何控制权.他们是第三方.此外,用户可以随时手动启动或停止它们.
我知道可执行文件的确切名称(例如"blah.exe")并且它们将始终相同,但可执行文件的路径不一定如此.
批处理文件包装器在这里是不可行的.
其他应用程序不是 Java应用程序,只是普通的旧Windows可执行文件.
为了避免潜在的锁定文件/崩溃问题,可以启动服务器并捕获端口冲突。这些服务器在系统关闭时自动停止(即使在崩溃之后)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我猜你没有控制其他两个应用程序...如果你这样做,这不会太糟糕 - 你可以让他们听一个套接字,看看当你来时套接字是否可用起来.
下一个解决方案实际上可能与语言无关.您可以通过批处理文件包装器管理整个系统.编写一个批处理文件,在启动时创建一个文件,并在文件停止时删除它.Unix系统使用这种技术很多 - 他们经常将文件称为锁定文件.
如果只有你的应用程序会启动这些其他应用程序,那么你可以简单地跟踪你是否已经启动它,所以我猜这是不可能的,或者你不会问,所以我假设用户可能已经通过其他一些机制启动了这些程序.
如果您无法控制其他应用程序的启动,甚至无法编写批处理文件来启动它们,那么您就无法做您想做的事情(注意,应用程序必须始终使用批处理文件,即使用户手动启动它们).
我只是一个最后的努力可能是获得进程状态并解析它,但你必须确切知道PS中其他应用程序的调用,这不是真正的微不足道.此外,所有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){}
}
}
}
Run Code Online (Sandbox Code Playgroud)
编译并运行它.然后打开一个新的终端,并尝试再次运行它,而另一个正在运行,它不会.
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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
上面代码的策略是保持PID从上次运行开始,如果发现在系统上运行的PID,则不启动.如果完成,请重置.
首选项存储在Windows注册表中 HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs
我建议不要使用文件锁定来确保java应用程序不会同时运行两次,因为如果程序崩溃或永久挂起并被杀死,那么锁定会处于不一致状态,甚至可能在重新启动后仍然存在会导致问题,因为程序将如何知道仍在运行的程序与已崩溃并锁定锁定文件的程序之间的区别?
| 归档时间: |
|
| 查看次数: |
20351 次 |
| 最近记录: |