简体   繁体   English

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

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

I need to prevent users from starting my Java application (WebStart Swing app) multiple times.我需要防止用户多次启动我的 Java 应用程序(WebStart Swing 应用程序)。 So if the application is already running it shouldn't be possible to start it again or show a warning / be closed again.因此,如果应用程序已经在运行,则不可能再次启动它或显示警告/再次关闭。

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

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.1 which 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.理想情况下,您应该在文件中或通过命令行开关配置使用的端口,以防发生冲突。

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.此服务在 1.5 中可用。 Note that 1.5 is currently most of the way through its End Of Service Life period.请注意,1.5 目前已进入其使用寿命终止期。 Get with Java SE 6!使用 Java SE 6!

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 应用程序或小程序中使用文件锁定,则需要调用应用程序或小程序。

You can use JUnique library.您可以使用 JUnique 库。 It provides support for running single-instance java application and is open-source.它提供对运行单实例 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 应用程序中的完整回答

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/jneat and 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

We do the same in C++ by creating a kernal mutex object and looking for it at start up.我们在 C++ 中通过创建内核互斥对象并在启动时查找它来执行相同的操作。 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.优点与使用套接字相同,即当进程死亡/崩溃/退出/被杀死时,内核会清除互斥对象。

I'm not a Java programmer, so I am not sure whether you can do the same kind of thing in Java?我不是Java程序员,所以我不确定你是否可以用Java做同样的事情?

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

You could use the registry, although this halfheartedly defeats the purpose of using a high-level language like java.您可以使用注册表,尽管这会半心半意地破坏了使用像 java 这样的高级语言的目的。 At least your target platform is windows =D至少你的目标平台是 windows =D

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.现在,如果您终止程序,它不会删除锁定文件,但您可以通过将程序的 PID 写入锁定文件并使 lock() 方法检查该进程是否已在运行来解决此问题。 This is left as an assingment for anyone interested.这留给任何感兴趣的人作为评估。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM