如何允许一次只运行一个 Java 程序实例?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/920386/
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 21:05:14  来源:igfitidea点击:

How to allow running only one instance of a Java program at a time?

javajava-web-startjnlp

提问by r?ph

I need to prevent users from starting my Java application (WebStart Swing app) multiple times. So if the application is already running it shouldn't be possible to start it again or show a warning / be closed again.

我需要防止用户多次启动我的 Java 应用程序(WebStart Swing 应用程序)。因此,如果应用程序已经在运行,则不可能再次启动它或显示警告/再次关闭。

Is there some convenient way to achieve this? I thought about blocking a port or write sth to a file. But hopefully you can access some system properties or the JVM?

有没有一些方便的方法来实现这一目标?我想过阻塞端口或将某事写入文件。但希望您可以访问某些系统属性或 JVM?

btw. target platform is Windows XP with Java 1.5

顺便提一句。目标平台是带有 Java 1.5 的 Windows XP

采纳答案by Dave Webb

I think your suggestion of opening a port to listen when you start your application is the best idea.

我认为您建议在启动应用程序时打开端口进行侦听是最好的主意。

It's very easy to do and you don't need to worry about cleaning it up when you close your application. For example, if you write to a file but someone then kills the processes using Task Manager the file won't get deleted.

这很容易做到,您无需担心在关闭应用程序时将其清理干净。例如,如果您写入文件但有人使用任务管理器终止进程,则该文件不会被删除。

Also, if I remember correctly there is no easy way of getting the PID of a Java process from inside the JVM so don't try and formulate a solution using PIDs.

另外,如果我没记错的话,没有简单的方法可以从 JVM 内部获取 Java 进程的 PID,所以不要尝试使用 PID 来制定解决方案。

Something like this should do the trick:

像这样的事情应该可以解决问题:

private static final int PORT = 9999;
private static ServerSocket socket;    

private static void checkIfRunning() {
  try {
    //Bind to localhost adapter with a zero connection queue 
    socket = new ServerSocket(PORT,0,InetAddress.getByAddress(new byte[] {127,0,0,1}));
  }
  catch (BindException e) {
    System.err.println("Already running.");
    System.exit(1);
  }
  catch (IOException e) {
    System.err.println("Unexpected error.");
    e.printStackTrace();
    System.exit(2);
  }
}

This sample code explicitly binds to 127.0.0.1which should avoid any firewall warnings, as any traffic on this address must be from the local system.

此示例代码明确绑定到127.0.0.1哪个应避免任何防火墙警告,因为此地址上的任何流量必须来自本地系统。

When picking a port try to avoid one mentioned in the list of Well Known Ports. You should ideally make the port used configurable in a file or via a command line switch in case of conflicts.

在选择端口时,尽量避免在众所周知的端口列表中提到的端口。理想情况下,您应该在文件中或通过命令行开关配置使用的端口,以防发生冲突。

回答by codebliss

You could use the registry, although this halfheartedly defeats the purpose of using a high-level language like java. At least your target platform is windows =D

您可以使用注册表,尽管这会半心半意地破坏了使用像 java 这样的高级语言的目的。至少你的目标平台是 windows =D

回答by Colin Desmond

We do the same in C++ by creating a kernal mutex object and looking for it at start up. The advantages are the same as using a socket, ie when the process dies/crashes/exits/is killed, the mutex object is cleaned up by the kernel.

我们在 C++ 中通过创建内核互斥对象并在启动时查找它来执行相同的操作。优点与使用套接字相同,即当进程死亡/崩溃/退出/被杀死时,内核会清除互斥对象。

I'm not a Java programmer, so I am not sure whether you can do the same kind of thing in Java?

我不是Java程序员,所以我不确定你是否可以用Java做同样的事情?

回答by Tom Hawtin - tackline

As the question states that WebStart is being used, the obvious solution is to use javax.jnlp.SingleInstanceService.

由于问题表明正在使用 WebStart,显而易见的解决方案是使用javax.jnlp.SingleInstanceService.

This service is available in 1.5. Note that 1.5 is currently most of the way through its End Of Service Life period. Get with Java SE 6!

此服务在 1.5 中可用。请注意,1.5 目前已进入其使用寿命终止期。使用 Java SE 6!

回答by Piotr Kochański

I think that the better idea would be to use file lock (quite an old idea :) ). Since Java 1.4 a new I/O library was introduced, that allows file locking.

我认为更好的主意是使用文件锁(一个很老的主意:))。由于 Java 1.4 引入了一个新的 I/O 库,它允许文件锁定。

Once the application starts it tries to acquire lock on a file (or create it if does not exist), when the application exits the lock is relased. If application cannot acquire a lock, it quits.

一旦应用程序启动,它就会尝试获取一个文件的锁(如果不存在则创建它),当应用程序退出时,锁被释放。如果应用程序无法获取锁,则退出。

The example how to do file locking is for example in Java Developers Almanac.

例如在Java Developers Almanac 中如何进行文件锁定的示例。

If you want to use file locking in Java Web Start application or an applet you need to sing the application or the applet.

如果要在 Java Web Start 应用程序或小程序中使用文件锁定,则需要调用应用程序或小程序。

回答by rumatoest

I've create the cross platform AppLock class.

我已经创建了跨平台 AppLock 类。

http://mixeddev.info/articles/2015/02/01/run-single-jvm-app-instance.html

http://mixeddev.info/articles/2015/02/01/run-single-jvm-app-instance.html

It is using file lock technique.

它正在使用文件锁定技术。

Update. At 2016-10-14 I've created package compatible with maven/gradle https://github.com/jneat/jneatand explained it here http://mixeddev.info/articles/2015/06/01/synchronize-different-jvm-instances.html

更新。在 2016 年 10 月 14 日,我创建了与 maven/gradle 兼容的包https://github.com/jneat/jneat并在此处进行了解释http://mixeddev.info/articles/2015/06/01/synchronize-different -jvm-instances.html

回答by Nirro

I've seen so many of this questions and I was looking to solve the same problem in a platform independent way that doesn't take the chance to collide with firewalls or get into socket stuff.

我已经看到了很多这样的问题,我希望以一种独立于平台的方式解决同样的问题,这种方式不会冒着与防火墙冲突或进入套接字的机会。

So, here's what I did:

所以,这就是我所做的:

import java.io.File;
import java.io.IOException;

/**
 * This static class is in charge of file-locking the program
 * so no more than one instance can be run at the same time.
 * @author nirei
 */
public class SingleInstanceLock {

    private static final String LOCK_FILEPATH = System.getProperty("java.io.tmpdir") + File.separator + "lector.lock";
    private static final File lock = new File(LOCK_FILEPATH);
    private static boolean locked = false;

    private SingleInstanceLock() {}

    /**
     * Creates the lock file if it's not present and requests its deletion on
     * program termination or informs that the program is already running if
     * that's the case.
     * @return true - if the operation was succesful or if the program already has the lock.<br>
     * false - if the program is already running
     * @throws IOException if the lock file cannot be created.
     */
    public static boolean lock() throws IOException {
        if(locked) return true;

        if(lock.exists()) return false;

        lock.createNewFile();
        lock.deleteOnExit();
        locked = true;
        return true;
    }
}

Using System.getProperty("java.io.tmpdir") for the lockfile path makes sure that you will always create your lock on the same place.

使用 System.getProperty("java.io.tmpdir") 作为锁文件路径可确保您始终在同一位置创建锁。

Then, from your program you just call something like:

然后,从您的程序中,您只需调用以下内容:

blah blah main(blah blah blah) {
    try() {
        if(!SingleInstanceLock.lock()) {
            System.out.println("The program is already running");
            System.exit(0);
        }
    } catch (IOException e) {
        System.err.println("Couldn't create lock file or w/e");
        System.exit(1);
    }
}

And that does it for me. Now, if you kill the program it won't delete the lock file but you can solve this by writing the program's PID into the lockfile and making the lock() method check if that process is already running. This is left as an assingment for anyone interested. :)

这对我有用。现在,如果您终止程序,它不会删除锁定文件,但您可以通过将程序的 PID 写入锁定文件并使 lock() 方法检查该进程是否已在运行来解决此问题。这留给任何感兴趣的人作为评估。:)

回答by kolobok

You can use JUnique library. It provides support for running single-instance java application and is open-source.

您可以使用 JUnique 库。它提供对运行单实例 java 应用程序的支持,并且是开源的。

http://www.sauronsoftware.it/projects/junique/

http://www.sauronsoftware.it/projects/junique/

See also my full answer at How to implement a single instance Java application?

另请参阅我在如何实现单实例 Java 应用程序中的完整回答

回答by Jalal Hussain Barzi

Try JUnique:

试试 JUnique:

String appId = "com.example.win.run.main";
boolean alreadyRunning;
try {
    JUnique.acquireLock(appId);
    alreadyRunning = false;
} catch (AlreadyLockedException e) {
    alreadyRunning = true;
}
if (alreadyRunning) {
    Sysout("An Instance of this app is already running");
    System.exit(1);
}