如何实现单实例Java应用程序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/177189/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 10:55:07  来源:igfitidea点击:

How to implement a single instance Java application?

javasingle-instance

提问by Fuangwith S.

Sometime I see many application such as msn, windows media player etc that are single instance applications (when user executes while application is running a new application instance will not created).

有时我会看到许多应用程序,例如 msn、windows media player 等,它们都是单实例应用程序(当用户在应用程序运行时执行时,不会创建新的应用程序实例)。

In C#, I use Mutexclass for this but I don't know how to do this in Java.

在 C# 中,我Mutex为此使用类,但我不知道如何在 Java 中执行此操作。

采纳答案by VonC

If I believe this article, by :

如果我相信这篇文章,作者:

having the first instance attempt to open a listening socket on the localhost interface. If it's able to open the socket, it is assumed that this is the first instance of the application to be launched. If not, the assumption is that an instance of this application is already running. The new instance must notify the existing instance that a launch was attempted, then exit. The existing instance takes over after receiving the notification and fires an event to the listener that handles the action.

让第一个实例尝试在 localhost 接口上打开一个监听套接字。如果它能够打开套接字,则假定这是要启动的应用程序的第一个实例。如果不是,则假设此应用程序的实例已在运行。新实例必须通知现有实例已尝试启动,然后退出。现有实例在收到通知后接管并向处理该操作的侦听器触发事件​​。

Note: Ahementions in the comment that using InetAddress.getLocalHost()can be tricky:

注意:Ahe在评论中提到使用InetAddress.getLocalHost()可能会很棘手:

  • it does not work as expected in DHCP-environment because address returned depends on whether the computer has network access.
    Solution was to open connection with InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
    Probably related to bug 4435662.
  • 它在 DHCP 环境中无法正常工作,因为返回的地址取决于计算机是否可以访问网络。
    解决方案是打开连接InetAddress.getByAddress(new byte[] {127, 0, 0, 1})
    可能与错误 4435662相关。
  • I also found bug 4665037which reports than Expected results of getLocalHost: return IP address of machine, vs. Actual results : return 127.0.0.1.
  • 我还发现了错误 4665037,它报告了比预期结果getLocalHost:返回机器的 IP 地址,与实际结果:返回127.0.0.1

it is surprising to have getLocalHostreturn 127.0.0.1on Linux but not on windows.

令人惊讶的是,在 Linux 上有getLocalHost回报127.0.0.1,但在 Windows 上却没有。



Or you may use ManagementFactoryobject. As explained here:

或者你可以使用ManagementFactory对象。正如这里所解释的:

The getMonitoredVMs(int processPid)method receives as parameter the current application PID, and catch the application name that is called from command line, for example, the application was started from c:\java\app\test.jarpath, then the value variable is "c:\\java\\app\\test.jar". This way, we will catch just application name on the line 17 of the code below.
After that, we search JVM for another process with the same name, if we found it and the application PID is different, it means that is the second application instance.

getMonitoredVMs(int processPid)方法接收当前应用程序PID作为参数,并捕获从命令行调用的应用程序名称,例如,应用程序从c:\java\app\test.jar路径启动,则值变量为“ c:\\java\\app\\test.jar”。这样,我们将在下面代码的第 17 行捕获应用程序名称。
之后,我们在JVM中搜索另一个同名的进程,如果找到它并且应用程序PID不同,则表示这是第二个应用程序实例。

JNLP offers also a SingleInstanceListener

JNLP 还提供了一个 SingleInstanceListener

回答by Kevin Day

We use file locking for this (grab an exclusive lock on a magic file in the user's app data directory), but we are primarily interested in preventing multiple instances from ever running.

我们为此使用文件锁定(在用户的应用程序数据目录中获取一个魔法文件的排他锁),但我们主要对防止多个实例运行感兴趣。

If you are trying to have the second instance pass command line args, etc... to the first instance, then using a socket connection on localhost will be killing two birds with one stone. General algorithm:

如果您试图让第二个实例将命令行参数等传递给第一个实例,那么在 localhost 上使用套接字连接将一石激起千层浪。通用算法:

  • On launch, try to open listener on port XXXX on localhost
  • if fail, open a writer to that port on localhost and send the command line args, then shutdown
  • otherwise, listen on port XXXXX on localhost. When receive command line args, process them as if the app was launched with that command line.
  • 启动时,尝试在本地主机上的端口 XXXX 上打开侦听器
  • 如果失败,请打开本地主机上该端口的写入器并发送命令行参数,然后关闭
  • 否则,在本地主机上监听端口 XXXXX。当接收命令行参数时,处理它们就好像应用程序是使用该命令行启动的一样。

回答by anjanb

You can open a Memory Mapped File and then see if that file is OPEN already. if it is already open, you can return from main.

您可以打开内存映射文件,然后查看该文件是否已打开。如果它已经打开,你可以从 main 返回。

Other ways is to use lock files(standard unix practice). One more way is to put something into the clipboard when main starts after checking if something is already in the clipboard.

其他方法是使用锁定文件(标准的 unix 实践)。另一种方法是在检查剪贴板中是否已有内容后,在 main 启动时将内容放入剪贴板。

Else, you can open a socket in a listen mode(ServerSocket). First try to connect to hte socket ; if you cannot connect, then open a serversocket. if you connect, then you know that another instance is already running.

否则,您可以以侦听模式(ServerSocket)打开套接字。首先尝试连接到 hte 套接字;如果您无法连接,则打开一个服务器套接字。如果您连接,则您知道另一个实例已在运行。

So, pretty much any system resource can be used for knowing that an app is running.

因此,几乎任何系统资源都可用于了解应用程序正在运行。

BR, ~A

BR,~A

回答by Fuangwith S.

ManagementFactory class supported in J2SE 5.0 or later detail

J2SE 5.0 或更高版本中支持的 ManagementFactory 类详细信息

but now i use J2SE 1.4 and I found this one http://audiprimadhanty.wordpress.com/2008/06/30/ensuring-one-instance-of-application-running-at-one-time/but I never test. What do you think about it?

但现在我使用 J2SE 1.4,我发现了这个http://audiprimadhanty.wordpress.com/2008/06/30/ensuring-one-instance-of-application-running-at-one-time/但我从来没有测试过。你怎么看待这件事?

回答by Javamann

You could try using the Preferences API. It is platform independent.

您可以尝试使用 Preferences API。它是平台独立的。

回答by Jacek Szymański

On Windows, you can use launch4j.

在 Windows 上,您可以使用launch4j

回答by Ikon

I have found a solution, a bit cartoonish explanation, but still works in most cases. It uses the plain old lock file creating stuff, but in a quite different view:

我找到了一个解决方案,有点卡通化的解释,但在大多数情况下仍然有效。它使用普通的旧锁文件创建内容,但在一个完全不同的视图中:

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

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

I think it will be a help to those with a strict firewall setting.

我认为这对那些有严格防火墙设置的人会有所帮助。

回答by adrian.tarau

I used sockets for that and depending if the application is on the client side or server side the behavior is a bit different:

我为此使用了套接字,并且根据应用程序是在客户端还是服务器端,行为略有不同:

  • client side : if an instance already exists(I cannot listen on a specific port) I will pass the application parameters and exit(you may want to perform some actions in the previous instance) if not I will start the application.
  • server side : if an instance already exists I will print a message and exit, if not I will start the application.
  • 客户端:如果实例已经存在(我无法侦听特定端口),我将传递应用程序参数并退出(您可能希望在前一个实例中执行某些操作),否则我将启动应用程序。
  • 服务器端:如果一个实例已经存在,我将打印一条消息并退出,否则我将启动应用程序。

回答by Robert

I use the following method in the main method. This is the simplest, most robust, and least intrusive method I have seen so I thought that I'd share it.

我在主方法中使用以下方法。这是我见过的最简单、最健壮、干扰最少的方法,所以我想我会分享它。

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 parvez Ahmad

Yes this is a really decent answer for eclipse RCP eclipse single instance application below is my code

是的,这是 eclipse RCP eclipse 单实例应用程序的一个非常不错的答案,下面是我的代码

in application.java

在 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;
}