msn、Windows Media Player など、シングル インスタンス アプリケーションである多くのアプリケーションを目にすることがあります (アプリケーションの実行中にユーザーが実行すると、新しいアプリケーション インスタンスは作成されません)。
C# ではこれにMutex
クラスを使用しますが、Java でこれを行う方法がわかりません。
msn、Windows Media Player など、シングル インスタンス アプリケーションである多くのアプリケーションを目にすることがあります (アプリケーションの実行中にユーザーが実行すると、新しいアプリケーション インスタンスは作成されません)。
C# ではこれにMutex
クラスを使用しますが、Java でこれを行う方法がわかりません。
メインメソッドでは次のメソッドを使用します。これは、私が見た中で最も単純で、最も堅牢で、最も邪魔にならない方法なので、共有したいと思いました。
private static boolean lockInstance(final String lockFile) {
try {
final File file = new File(lockFile);
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: " + lockFile, e);
}
return false;
}
私がこの記事を信じるなら、by :
最初のインスタンスで、localhost インターフェイスでリスニング ソケットを開こうとします。ソケットを開くことができた場合、これが起動されるアプリケーションの最初のインスタンスであると見なされます。そうでない場合は、このアプリケーションのインスタンスが既に実行されていると想定されます。新しいインスタンスは、起動が試行されたことを既存のインスタンスに通知してから終了する必要があります。通知を受け取った後、既存のインスタンスが引き継ぎ、アクションを処理するリスナーに対してイベントを発生させます。
注: Aheはコメントで、使用InetAddress.getLocalHost()
が難しい場合があると述べています。
- 返されるアドレスはコンピュータがネットワークにアクセスできるかどうかに依存するため、DHCP 環境では期待どおりに動作しません。
解決策は、との接続を開くことでしたInetAddress.getByAddress(new byte[] {127, 0, 0, 1})
。
おそらくバグ 4435662に関連しています。
getLocalHost
127.0.0.1
getLocalHost
Linux ではリターン127.0.0.1
があるのに、Windows ではリターンがないというのは驚くべきことです。
または、ManagementFactory
オブジェクトを使用することもできます。ここで説明したように:
この
getMonitoredVMs(int processPid)
メソッドは、現在のアプリケーション PID をパラメーターとして受け取り、コマンド ラインから呼び出されたアプリケーション名をキャッチします。たとえば、アプリケーションがc:\java\app\test.jar
パスから開始された場合、値変数は "c:\\java\\app\\test.jar
" です。このようにして、以下のコードの 17 行目でアプリケーション名だけをキャッチします。
その後、JVM で同じ名前の別のプロセスを検索します。それが見つかり、アプリケーション PID が異なる場合は、それが 2 番目のアプリケーション インスタンスであることを意味します。
JNLP は、SingleInstanceListener
アプリなら。には GUI があり、JWS で起動し、SingleInstanceService
.
Java Plug-In (アプレットと JWS アプリの両方に必要) は Oracle によって非推奨となり、JDK から削除されました。ブラウザーの製造元は、既にブラウザーから削除していました。
したがって、この答えは無効です。古いドキュメントを見ている人に警告するためにここに残すだけです。
はい、これはeclipseRCPに対する本当にまともな答えです。以下のeclipseシングルインスタンスアプリケーションは私のコードです。
application.javaで
if(!isFileshipAlreadyRunning()){
MessageDialog.openError(display.getActiveShell(), "Fileship already running", "Another instance of this application is already running. Exiting.");
return IApplication.EXIT_OK;
}
private static boolean isFileshipAlreadyRunning() {
// socket concept is shown at http://www.rbgrn.net/content/43-java-single-application-instance
// but this one is really great
try {
final File file = new File("FileshipReserved.txt");
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: " + lockFile, e);
}
return false;
}
これにはファイル ロックを使用します (ユーザーのアプリ データ ディレクトリにあるマジック ファイルの排他ロックを取得します) が、複数のインスタンスが実行されないようにすることが主な目的です。
2 番目のインスタンスがコマンド ライン引数などを最初のインスタンスに渡そうとする場合、localhost でソケット接続を使用すると、一石二鳥です。一般的なアルゴリズム:
JUnique ライブラリを使用できます。単一インスタンスの Java アプリケーションの実行をサポートし、オープンソースです。
http://www.sauronsoftware.it/projects/junique/
JUnique ライブラリを使用すると、ユーザーが同じ Java アプリケーションの複数のインスタンスを同時に実行することを防止できます。
JUnique は、同じユーザーによって起動されたすべての JVM インスタンス間で共有されるロックと通信チャネルを実装します。
public static void main(String[] args) {
String appId = "myapplicationid";
boolean alreadyRunning;
try {
JUnique.acquireLock(appId, new MessageHandler() {
public String handle(String message) {
// A brand new argument received! Handle it!
return null;
}
});
alreadyRunning = false;
} catch (AlreadyLockedException e) {
alreadyRunning = true;
}
if (!alreadyRunning) {
// Start sequence here
} else {
for (int i = 0; i < args.length; i++) {
JUnique.sendMessage(appId, args[0]));
}
}
}
内部的には、%USER_DATA%/.junique フォルダーにファイル ロックを作成し、一意の appId ごとにランダム ポートにサーバー ソケットを作成して、Java アプリケーション間でメッセージを送受信できるようにします。
私は解決策、少し漫画的な説明を見つけましたが、ほとんどの場合はまだ機能します。それはものを作成する普通の古いロックファイルを使用しますが、まったく異なるビューで:
http://javalandscape.blogspot.com/2008/07/single-instance-from-your-application.html
ファイアーウォールの設定が厳しい方には参考になると思います。
Windowsでは、launch4jを使用できます。
J2SE 5.0以降でサポートされているManagementFactoryクラスの詳細
しかし今、私はJ2SE 1.4を使用しており、これを見つけましたhttp://audiprimadhanty.wordpress.com/2008/06/30/ensuring-one-instance-of-application-running-at-one-time/しかし、私はテストしません。あなたはそれについてどう思いますか?
そのためにソケットを使用しましたが、アプリケーションがクライアント側にあるかサーバー側にあるかによって、動作は少し異なります。
PreferencesAPIを使用してみてください。プラットフォームに依存しません。
単一のマシンまたはネットワーク全体のインスタンス数を制限するより一般的な方法は、マルチキャスト ソケットを使用することです。
マルチキャスト ソケットを使用すると、アプリケーションの任意の数のインスタンスにメッセージをブロードキャストできます。その一部は、企業ネットワークを介して物理的に離れたマシン上にある場合があります。
このようにして、多くのタイプの構成を有効にして、次のようなものを制御できます
Java のマルチキャスト サポートは、 MulticastSocketとDatagramSocketが主なツールであるjava.net パッケージを介して行われます。
注: MulticastSocket はデータ パケットの配信を保証しないため、JGroupsなどのマルチキャスト ソケットの上に構築されたツールを使用する必要があります。JGroupsはすべてのデータの配信を保証します。これは、非常に単純な API を備えた 1 つの jar ファイルです。
JGroups はしばらく前から存在しており、業界で印象的な用途がいくつかあります。たとえば、JBoss のクラスタリング メカニズムを支え、クラスタのすべてのインスタンスにデータをブロードキャストします。
JGroups を使用して、アプリのインスタンス数を (マシン上またはネットワーク上で、顧客が購入したライセンス数まで) 制限することは、概念的には非常に簡単です。
メモリ マップ ファイルを開いて、そのファイルが既に開いているかどうかを確認できます。すでに開いている場合は、メインから戻ることができます。
他の方法は、ロック ファイルを使用することです (標準的な UNIX の慣行)。もう 1 つの方法は、何かが既にクリップボードにあるかどうかを確認した後、main の開始時に何かをクリップボードに入れることです。
それ以外の場合は、リッスン モード (ServerSocket) でソケットを開くことができます。最初にソケットへの接続を試みます。接続できない場合は、サーバーソケットを開きます。接続すると、別のインスタンスがすでに実行されていることがわかります。
そのため、ほとんどすべてのシステム リソースを使用して、アプリが実行されていることを知ることができます。
BR、~A
パブリッククラスシングルインスタンス{ public static final String LOCK = System.getProperty("user.home") + File.separator + "test.lock"; public static final String PIPE = System.getProperty("user.home") + File.separator + "test.pipe"; プライベート静的 JFrame フレーム = null; public static void main(String[] args) { 試す { FileChannel lockChannel = new RandomAccessFile(LOCK, "rw").getChannel(); FileLock flk = null; 試す { flk = lockChannel.tryLock(); } catch(スロー可能な t) { t.printStackTrace(); } if (flk == null || !flk.isValid()) { System.out.println("実行中です。パイプにメッセージを残して終了しています..."); FileChannel pipeChannel = null; 試す { pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel(); MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1); bb.put(0, (バイト)1); bb.force(); } キャッチ (スロー可能な t) { t.printStackTrace(); } 最後に { if (pipeChannel != null) { 試す { pipeChannel.close(); } キャッチ (スロー可能な t) { t.printStackTrace(); } } } System.exit(0); } //ここではロックを解除してチャネルを閉じません。 // これは、アプリケーションがクラッシュするか正常に終了した後に実行されます。 SwingUtilities.invokeLater( 新しいランナブル() { public void run() { createAndShowGUI(); } } ); FileChannel pipeChannel = null; 試す { pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel(); MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1); while (真) { バイト b = bb.get(0); もし (b > 0) { bb.put(0, (バイト)0); bb.force(); SwingUtilities.invokeLater( 新しいランナブル() { public void run() { frame.setExtendedState(JFrame.NORMAL); frame.setAlwaysOnTop(true); frame.toFront(); frame.setAlwaysOnTop(false); } } ); } Thread.sleep(1000); } } キャッチ (スロー可能な t) { t.printStackTrace(); } 最後に { if (pipeChannel != null) { 試す { pipeChannel.close(); } キャッチ (スロー可能な t) { t.printStackTrace(); } } } } catch(スロー可能な t) { t.printStackTrace(); } } public static void createAndShowGUI() { フレーム = 新しい JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(800, 650); frame.getContentPane().add(new JLabel("メインウィンドウ", SwingConstants.CENTER)、BorderLayout.CENTER); frame.setLocationRelativeTo(null); frame.setVisible(真); } }