既に実行されている Java アプリケーションが多重起動出来ないようにする方法。
まず、main() メソッドの冒頭に、以下のように記述する。
IOException の処理も忘れずに。
FileOutputStream fos = new FileOutputStream("./temp/lockfile"); FileChannel fchan = fos.getChannel(); FileLock flock = fchan.tryLock(); if (null == flock) { // 例外 throw 等、ロックが取得できなかったときの処理を記述 }
ファイル"./temp/lockfile"はロック用一時ファイルであり、このファイルに対するロックを取得を試みることで、プログラムが既に起動されているか否かを判定する。
ロック取得できなかった場合、変数 flock は null となるので、例外を throw するなどして、プログラムを終了させればよい。
尚、プログラムが終了する際は、取得していたロックを解放してロック用一時ファイルを削除する必要がある。この処理は、シャットダウンフックを使って実装する。
プログラム開始時のロック取得、終了時のロック解放をユーティリティクラス化してみると、以下のような実装となる。
//*** Java SE import java.io.*; import java.nio.channels.*; /** * @author おやつ丸 */ public final class LockUtility { private LockUtility () { // インスタンスの生成は許可しない。 } /** * ロックの取得を試みます。<br/> * ロック取得に失敗した場合は、AlreadyBootedException を throw します。 * * @return */ public static synchronized boolean lock() throws IOException { try { fos = new FileOutputStream("./temp/lockfile"); fchan = fos.getChannel(); flock = fchan.tryLock(); } catch (IOException e) { throw e } if (null == flock) { throw new AlreadyBootedException(); } // ロック解放処理をシャットダウンフックに登録 Runtime.getRuntime().addShutdownHook(new Thread(new LockShutdownHook())); return true; } /** * ロックを解放します。 */ public static void release() { try { flock.release(); fchan.close(); fos.close(); new File("./temp/lockfile").delete(); } catch (IOException e) { e.printStackTrace(); } return; } //*** クラス変数 private static FileOutputStream fos; private static FileChannel fchan; private static FileLock flock; } class LockShutdownHook implements Runnable { /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { LockUtility.release(); return; } } class AlreadyBootedException extends RuntimeException { AlreadyBootedException() { super("プログラムの多重起動は出来ません。"); return; } }
2011.09.10 追記
停電でPCが落ちてロック用一時ファイルが残ったままになった…だと?
UPSでも導入してくれ。