アプリケーションに、それ自体の別のバージョンがすでに実行されているかどうかを確認してもらいたい。
たとえば、demo.jar
開始すると、ユーザーはクリックして再度実行しますが、2番目のインスタンスは「まあ、すでにdemo.jar
実行中です」と認識します。そしてメッセージで終了します。
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秒待ちます。これにより、2番目の端末で実行できるようになります。
キル-9でプログラムを強制的に停止した後にテストします
結論:
プログラムが動作しなくなると、ソケットの占有はオペレーティングシステムによってクリーンアップされます。したがって、プログラムが2回実行されないことを確認できます。
欠点
卑劣な人、またはいたずらなプロセスがすべてのポート、またはポートだけをバインドする場合、プログラムはすでに実行されていると見なされるため、プログラムは実行されません。
あなたが探していることは、おそらくロックファイルで最もよく達成することができます。ロックファイルとは、事前定義された場所を持ち、その存在がミューテックスであるファイルを意味します。
プログラムの起動時にそのファイルが存在するかどうかをテストし、存在する場合はすぐに終了します。既知の場所にファイルを作成します。プログラムが正常に終了する場合は、ロックファイルを削除してください。
おそらく最良の方法は、ファイルにpid(プロセスID)を入力して、ファイルを削除しなかったがOS固有の異常な終了を検出できるようにすることです。
シンプルでありながら強力なテスト済みソリューション。
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リスナーなどを設定する必要はありません...
Mutexを使用する場合、論理的には、「プログラム」のコピーを実行していた任意のJVMからMutexにアクセスできる必要があります。Cプログラミングでは、これは共有メモリを介して実行される可能性がありますが、Javaにはデフォルトではそのような機能はありません。
その理解があれば、必要なものを実装する方法はたくさんあります。指定されたポートでサーバーソケットを開くことができます(オペレーティングシステムは、1つのプロセスのみがサーバーソケットの受信者であることを保証し、その後のオープンは失敗します)。
「ロックファイル」を使用することもできますが、使用する必要のあるファイルは実際にはディレクトリであるため、少し複雑です(ほとんどのディレクトリ作成では、ディレクトリ作成がファイルシステムにとってアトミックであるかどうかに大きく依存します)。それは)。システム管理者がNFS経由で実行することを決定した場合、事態はさらに困難になります(不可能ではないにしても)。
関連するすべてのJVMが同じ構成で起動されることを何らかの方法で保証できる場合は、JVMとデバッグ/ JMIを使用して多くの巧妙なトリックを実行することもできます(やがて不可能なタスク)。
他の人はexec機能を使用してプロセスリストに相当するものを実行しましたが、競合状態の可能性があるため、少し注意が必要です(2つのプロセスが同時にチェックし、お互いを確認できません)。
最終的に、サーバーソケットルートは、TCP / IPスタックによって1つのプロセスにのみバインドされることが保証されているため(オペレーティングシステムによって仲介されるため)、おそらく最も安定しています。とはいえ、着信メッセージのソケットをフラッシュする必要があり、それによって他のセキュリティ問題が発生する可能性があります。
アプリケーションが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」を指定できます。
編集:見た目ほど複雑ではありません:)そして私はそれがきれいだと思います。
このコードの戦略は、レジストリで最後に実行された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が強制終了され、プログラムが再実行されます。
他のいくつかの答えとは対照的に、最も信頼できる方法は、ServerSocket
自分だけが知っている固定ポート上に、ペイントカードの途中でを作成することです。他のロックファイルとは異なり、アプリケーションが終了すると自動的に解放されます。また、aを介した以前の存在BindException
は、別のインスタンスがすでに実行されていることを示すかなり間違いのない兆候です。
次の解決策は、2つの致命的なシーンでも機能します。1>タスクマネージャーでjavaw.exeとしてスケジュールされた起動されたexeでも。2>アプリケーションを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
これは、ユーザーのホームディレクトリにある自動的に名前が付けられたロックファイルを使用する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_]", "_");
}
}
これは、アプリケーションが一意の名前でタスクマネージャーでスケジュールできる場合にも適したソリューションです。
"tasklist /FI \"IMAGENAME eq "+MyApplication+".exe
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();
}
}