我希望我的应用程序检查其自身的另一个版本是否已在运行。
例如,demo.jar
已启动,用户点击再次运行它,但第二个实例意识到“哦等等,已经有demo.jar
正在运行。”并退出并留言。
答案 0 :(得分:9)
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 :(得分:5)
您正在寻找的内容可能最好通过锁定文件来完成。通过锁定文件,我只是指一个具有预定义位置并且其存在是您的互斥锁的文件。
在程序启动时测试该文件是否存在,如果存在,立即退出。在已知位置创建文件。如果程序正常退出,请删除锁定文件。
如果您还可以使用pid(进程ID)填充文件,那么最好的情况是,这样您就可以检测到没有删除文件的异常退出,但这会导致操作系统特定。
答案 2 :(得分: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监听器或其他......
答案 3 :(得分:2)
如果您使用Mutex,从逻辑上讲,Mutex需要可以从运行“程序”副本的任何JVM访问。在C编程中,这可以通过共享内存来完成,但Java默认没有这样的东西。
有了这种理解,有很多方法可以实现你想要的东西。您可以在指定端口上打开服务器套接字(操作系统确保只有一个进程是服务器套接字的接收者,后续打开失败)。
您可以使用“锁定文件”,但它有点复杂,因为您需要使用的文件实际上是一个目录(并且它在很大程度上取决于您的文件系统的目录创建是否是原子的,即使大多数目录创作是)。如果系统管理员决定通过NFS运行你,那么事情变得更加困难(如果不是不可能的话)。
你也可以使用JVM和调试/ JMI做一些漂亮的技巧,只要你能以某种方式确保所有相关的JVM都以相同的配置启动(及时,一个不可能完成的任务)。
其他人使用exec工具运行相当于进程列表,但由于竞争条件的可能性(两个进程同时检查,并且无法看到对方),这有点棘手。
最后,服务器套接字路由可能是最稳定的,因为它保证仅通过TCP / IP堆栈绑定到一个进程(并由操作系统调解)。也就是说,您将不得不刷新传入消息的套接字,并且可能会出现其他安全问题。
答案 4 :(得分: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”。
编辑:它没有看起来那么复杂:)我发现它很干净。
答案 5 :(得分:2)
此代码的策略是保持PID在注册表中的最后一次运行,如果发现在系统上运行该PID,则不启动。如果完成,请重置。
首选项存储在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将被终止,程序将重新运行。
答案 6 :(得分: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_]", "_");
}
}
```
答案 7 :(得分:0)
与其他几个答案相反,最可靠的方法是在只有您知道的固定端口上创建ServerSocket
,然后在油漆卡中。它将在您的应用程序退出时自动释放,与任何锁定文件不同,它之前通过BindException
存在是另一个实例已在运行的绝对绝对标志。
答案 8 :(得分:0)
以下解决方案也在两个致命的场景中工作。 1 GT;甚至你的已启动的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
答案 9 :(得分:0)
如果您的应用程序可以在任务管理器中使用唯一名称
进行调度,这也是一个很好的解决方案 "tasklist /FI \"IMAGENAME eq "+MyApplication+".exe
答案 10 :(得分: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();
}
}
答案 11 :(得分:-1)
FileLock在Linux上不起作用。请不要使用FileLock。我认为获取Process的名称(如果可能的话,通过创建一个唯一的名称)将是解决此问题的一种方法。我认为进程ID是自动分配的。
获取流程时请参阅:
http://www.itechp2pexchange.com/content/linux-unix-run-only-one-instance-script