90

msn、Windows Media Player など、シングル インスタンス アプリケーションである多くのアプリケーションを目にすることがあります (アプリケーションの実行中にユーザーが実行すると、新しいアプリケーション インスタンスは作成されません)。

C# ではこれにMutexクラスを使用しますが、Java でこれを行う方法がわかりません。

4

17 に答える 17

67

メインメソッドでは次のメソッドを使用します。これは、私が見た中で最も単純で、最も堅牢で、最も邪魔にならない方法なので、共有したいと思いました。

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;
}
于 2010-01-04T23:07:16.353 に答える
62

私がこの記事を信じるなら、by :

最初のインスタンスで、localhost インターフェイスでリスニング ソケットを開こうとします。ソケットを開くことができた場合、これが起動されるアプリケーションの最初のインスタンスであると見なされます。そうでない場合は、このアプリケーションのインスタンスが既に実行されていると想定されます。新しいインスタンスは、起動が試行されたことを既存のインスタンスに通知してから終了する必要があります。通知を受け取った後、既存のインスタンスが引き継ぎ、アクションを処理するリスナーに対してイベントを発生させます。

注: Aheはコメントで、使用InetAddress.getLocalHost()が難しい場合があると述べています。

  • 返されるアドレスはコンピュータがネットワークにアクセスできるかどうかに依存するため、DHCP 環境では期待どおりに動作しません。
    解決策は、との接続を開くことでしたInetAddress.getByAddress(new byte[] {127, 0, 0, 1})
    おそらくバグ 4435662に関連しています。
  • また、Expected results of : return IP address of machine, vs. Actual results : returnよりもレポートするバグ 4665037を見つけました。 getLocalHost127.0.0.1

getLocalHostLinux ではリターン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

于 2008-10-07T04:01:38.400 に答える
11

アプリなら。には GUI があり、JWS で起動し、SingleInstanceService.

アップデート

Java Plug-In (アプレットと JWS アプリの両方に必要) は Oracle によって非推奨となり、JDK から削除されました。ブラウザーの製造元は、既にブラウザーから削除していました。

したがって、この答えは無効です。古いドキュメントを見ている人に警告するためにここに残すだけです。

于 2011-08-24T14:04:53.303 に答える
6

はい、これは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;
}
于 2010-11-23T13:39:14.130 に答える
5

これにはファイル ロックを使用します (ユーザーのアプリ データ ディレクトリにあるマジック ファイルの排他ロックを取得します) が、複数のインスタンスが実行されないようにすることが主な目的です。

2 番目のインスタンスがコマンド ライン引数などを最初のインスタンスに渡そうとする場合、localhost でソケット接続を使用すると、一石二鳥です。一般的なアルゴリズム:

  • 起動時に、localhost のポート XXXX でリスナーを開こうとします。
  • 失敗した場合は、localhost のそのポートにライターを開き、コマンド ライン引数を送信してからシャットダウンします。
  • それ以外の場合は、localhost のポート XXXXX でリッスンします。コマンド ライン引数を受け取ると、アプリがそのコマンド ラインで起動されたかのように処理します。
于 2008-10-07T04:26:34.543 に答える
5

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 アプリケーション間でメッセージを送受信できるようにします。

于 2016-11-23T11:31:29.557 に答える
5

私は解決策、少し漫画的な説明を見つけましたが、ほとんどの場合はまだ機能します。それはものを作成する普通の古いロックファイルを使用しますが、まったく異なるビューで:

http://javalandscape.blogspot.com/2008/07/single-instance-from-your-application.html

ファイアーウォールの設定が厳しい方には参考になると思います。

于 2009-05-16T12:53:35.857 に答える
4

Windowsでは、launch4jを使用できます。

于 2008-10-07T09:01:11.323 に答える
3

J2SE 5.0以降でサポートされているManagementFactoryクラスの詳細

しかし今、私はJ2SE 1.4を使用しており、これを見つけましたhttp://audiprimadhanty.wordpress.com/2008/06/30/ensuring-one-instance-of-application-running-at-one-time/しかし、私はテストしません。あなたはそれについてどう思いますか?

于 2008-10-07T06:02:04.053 に答える
2

そのためにソケットを使用しましたが、アプリケーションがクライアント側にあるかサーバー側にあるかによって、動作は少し異なります。

  • クライアント側:インスタンスがすでに存在する場合(特定のポートでリッスンできない場合)、アプリケーションパラメータを渡し、終了します(前のインスタンスでいくつかのアクションを実行する場合があります)。そうでない場合は、アプリケーションを起動します。
  • サーバー側:インスタンスがすでに存在する場合はメッセージを出力して終了し、存在しない場合はアプリケーションを起動します。
于 2009-05-16T19:49:55.573 に答える
2

PreferencesAPIを使用してみてください。プラットフォームに依存しません。

于 2008-10-07T08:58:06.803 に答える
2

単一のマシンまたはネットワーク全体のインスタンス数を制限するより一般的な方法は、マルチキャスト ソケットを使用することです。

マルチキャスト ソケットを使用すると、アプリケーションの任意の数のインスタンスにメッセージをブロードキャストできます。その一部は、企業ネットワークを介して物理的に離れたマシン上にある場合があります。

このようにして、多くのタイプの構成を有効にして、次のようなものを制御できます

  • マシンごとに 1 つまたは複数のインスタンス
  • ネットワークごとに 1 つまたは複数のインスタンス (クライアント サイトでのインストールの制御など)

Java のマルチキャスト サポートは、 MulticastSocketDatagramSocketが主なツールであるjava.net パッケージを介して行われます。

: MulticastSocket はデータ パケットの配信を保証しないため、JGroupsなどのマルチキャスト ソケットの上に構築されたツールを使用する必要があります。JGroupsすべてのデータの配信を保証します。これは、非常に単純な API を備えた 1 つの jar ファイルです。

JGroups はしばらく前から存在しており、業界で印象的な用途がいくつかあります。たとえば、JBoss のクラスタリング メカニズムを支え、クラスタのすべてのインスタンスにデータをブロードキャストします。

JGroups を使用して、アプリのインスタンス数を (マシン上またはネットワーク上で、顧客が購入したライセンス数まで) 制限することは、概念的には非常に簡単です。

  • アプリケーションの起動時に、各インスタンスは「My Great App Group」などの名前付きグループに参加しようとします。このグループは、0、1、または N 人のメンバーを許可するように構成されています
  • グループ メンバーの数が設定した数よりも多い場合、アプリは起動を拒否する必要があります。
于 2016-04-22T08:53:30.490 に答える
2

メモリ マップ ファイルを開いて、そのファイルが既に開いているかどうかを確認できます。すでに開いている場合は、メインから戻ることができます。

他の方法は、ロック ファイルを使用することです (標準的な UNIX の慣行)。もう 1 つの方法は、何かが既にクリップボードにあるかどうかを確認した後、main の開始時に何かをクリップボードに入れることです。

それ以外の場合は、リッスン モード (ServerSocket) でソケットを開くことができます。最初にソケットへの接続を試みます。接続できない場合は、サーバーソケットを開きます。接続すると、別のインスタンスがすでに実行されていることがわかります。

そのため、ほとんどすべてのシステム リソースを使用して、アプリが実行されていることを知ることができます。

BR、~A

于 2008-10-07T04:33:40.723 に答える
1
パブリッククラスシングルインスタンス{
    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(真);
    }
}

于 2014-05-12T16:13:01.607 に答える