如何保证一个java程序只能执行一个实例?

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

How to ensure only one instance of a java program can be executed?

java

提问by Exploring

It is requited that only one instance of a JAVA program can be executed at a certain time. I have observed a good number of solutions proposed in stack overflow in different earlier posts.

要求在某一时刻只能执行一个JAVA程序实例。我在不同的早期帖子中观察到堆栈溢出中提出的大量解决方案。

The solutions are based on:

解决方案基于:

  • By opening socket: To open a socket connection.
  • Based on the file locking: To create a temporary file and to hold a lock. And to add a shutdown hook to unlock that file when the JVM shuts down.
  • 通过打开套接字:打开一个套接字连接。
  • 基于文件锁定:创建一个临时文件并持有一个锁。并添加关闭挂钩以在 JVM 关闭时解锁该文件。

I do not want to use port locking as it can cause a possible conflict in ports usage.

我不想使用端口锁定,因为它可能会导致端口使用发生冲突。

So I was thinking to use file locking. After searching a bit, I have found the proponents of the port locking based mechanism has mentioned file locking can be unreliable if application crashes and for other IO errors.

所以我想使用文件锁定。经过一番搜索,我发现基于端口锁定机制的支持者提到如果应用程序崩溃和其他 IO 错误,文件锁定可能不可靠。

What I need is to find a solution which will work consistently in cross platform and in multiple JDK. My intended platform is Windows and Linux and JDK is Sun and IBM JDK.

我需要的是找到一个可以在跨平台和多个 JDK 中一致工作的解决方案。我的目标平台是 Windows 和 Linux,JDK 是 Sun 和 IBM JDK。

Can anyone shed some light on to this?

任何人都可以对此有所了解吗?

回答by Rahul Tripathi

You may use ManagementFactory object. From here:-

您可以使用 ManagementFactory 对象。从这里:-

import sun.management.ConnectorAddressLink;  
import sun.jvmstat.monitor.HostIdentifier;  

import sun.jvmstat.monitor.Monitor;  
import sun.jvmstat.monitor.MonitoredHost;  

import sun.jvmstat.monitor.MonitoredVm;  
import sun.jvmstat.monitor.MonitoredVmUtil;  
import sun.jvmstat.monitor.MonitorException;  
import sun.jvmstat.monitor.VmIdentifier;  

public static void main(String args[]) {  
/* The method ManagementFactory.getRuntimeMXBean() returns an identifier with applcation PID
   in the Sun JVM, but each jvm may have you own implementation. 
   So in anothers jvm, other than Sun, this code may not work., :( 
*/  
 RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean();  
         final int runtimePid = Integer.parseInt(rt.getName().substring(0,rt.getName().indexOf("@")));  

  java.awt.EventQueue.invokeLater(new Runnable() {  
  public void run() {  

  // If exists another instance, show message and terminates the current instance.  
  // Otherwise starts application.  
  if (getMonitoredVMs(runtimePid))  
  {  
     new MainFrame().setVisible(true);  
  } else  
  JOptionPane.showMessageDialog(null,"There is another instance of this application running.");  

  }  
  });  
  }

The getMonitoredVMs(int processPid)method receives as paramter 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.jar path, then the value variable is "c:\java\app\teste.jar". This way, we will catch just application name on the line 17 of the code below. After that, we search JVM for antoher 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,并捕捉是从命令行调用,例如,该应用程序是从下开始的应用程序名称:\ java的\应用\ Test.jar的路径,则该值变量是“c:\java\app\teste.jar”。这样,我们将在下面代码的第 17 行中仅捕获应用程序名称。之后,我们在JVM中搜索另一个同名的进程,如果找到并且应用程序PID不同,则表示是第二个应用程序实例。

private static boolean getMonitoredVMs(int processPid) {  
         MonitoredHost host;  
         Set vms;  
try {  
     host = MonitoredHost.getMonitoredHost(new HostIdentifier((String)null));  
     vms = host.activeVms();  
    } catch (java.net.URISyntaxException sx) {  
 throw new InternalError(sx.getMessage());  
  } catch (MonitorException mx) {  
 throw new InternalError(mx.getMessage());  
 }  
 MonitoredVm mvm = null;  
 String processName = null;  
 try{  
     mvm = host.getMonitoredVm(new VmIdentifier(String.valueOf(processPid)));  
     processName = MonitoredVmUtil.commandLine(mvm);  
     processName = processName.substring(processName.lastIndexOf("\") + 1,processName.length());  
             mvm.detach();  
     } catch (Exception ex) {  

     }  
 // This line is just to verify the process name. It can be removed. 
  JOptionPane.showMessageDialog(null,processName);  
  for (Object vmid: vms) {  
  if (vmid instanceof Integer) {  
  int pid = ((Integer) vmid).intValue();  
  String name = vmid.toString(); // default to pid if name not available  
  try {  
      mvm = host.getMonitoredVm(new VmIdentifier(name));  
      // use the command line as the display name  
    name =  MonitoredVmUtil.commandLine(mvm);  
    name = name.substring(name.lastIndexOf("\")+1,name.length());  
    mvm.detach();  
    if ((name.equalsIgnoreCase(processName)) && (processPid != pid))  
    return false;  
   } catch (Exception x) {  
   // ignore  
   }  
   }  
   }  

   return true;  
   }

Also check the Using the SingleInstanceService Service

还要检查使用 SingleInstanceService 服务

The javax.jnlp.SingleInstanceServiceprovides a set of methods for applications to register themselves as singletons, and to register listener(s) for handling arguments passed in from different instances of applications.

所述javax.jnlp.SingleInstanceService提供了一组方法的应用程序将自身注册为单身,并注册监听器(一个或多个),用于处理从应用程序的不同实例中传递的参数。

回答by FabioDch

If you are familiar with C, you could address this issue with named pipes in windows and local socket in unix. Both of them require a little of JNI. These communication channel are resources of your application, thus when your application crashes OS has the duty of freeing your resources. Furthermore they are identified by a textual name, so the chance of name clashing is the same for file locking.

如果您熟悉 C,则可以使用 Windows 中的命名管道和 unix 中的本地套接字来解决此问题。它们都需要一点 JNI。这些通信通道是您的应用程序的资源,因此当您的应用程序崩溃时,操作系统有责任释放您的资源。此外,它们由文本名称标识,因此名称冲突的可能性对于文件锁定是相同的。

You can pickup an example of local socket in this stackoverflow answer.

您可以在此stackoverflow answer中选择本地套接字的示例。

An example of named pipe in windows could be found here

可以在此处找到 Windows 中命名管道的示例

回答by jdev

hi there are many way to do it , just visit this page. i just copy paste. also view this thread [stackoverflow][1]

嗨,有很多方法可以做到,只需访问此页面。我只是复制粘贴。另请查看此线程 [stackoverflow][1]

One of the most asking question in java world is how to make java application as a single instance.

Java 世界中最常见的问题之一是如何使 Java 应用程序成为单个实例。

I google it and found many of the techniques. I am posted here some of popular techniques. Just go ahead and contact if you have problem......

我用谷歌搜索并找到了许多技术。我在这里发布了一些流行的技术。如果您有问题,请继续联系......

  1. By capturing port or through ServerSocket (short code) . On this method we are creating a object of java.net.ServerSocket class. And by passing a port number we are captured while first instance so that if another instance occurred it is throwing a bind Exception and you can tracked that any more instance is running on system.
  1. 通过捕获端口或通过 ServerSocket(短代码)。在这个方法中,我们创建了一个 java.net.ServerSocket 类的对象。通过传递一个端口号,我们在第一个实例时被捕获,这样如果另一个实例发生,它就会抛出一个绑定异常,你可以跟踪系统上正在运行的任何更多实例。

Just see the link for code http://yuvadevelopers.dmon.com/java_examples/Single_Instance_small.htm

只需查看代码链接 http://yuvadevelopers.dmon.com/java_examples/Single_Instance_small.htm

  1. By capturing port or through ServerSocket (Big code). It is same as the first method but while google i got this big code with different option just go through the code.
  1. 通过捕获端口或通过 ServerSocket(大代码)。它与第一种方法相同,但是当谷歌我得到这个带有不同选项的大代码时,只需通过代码即可。

Just see the link for code See the original source here get from google http://www.rbgrn.net/blog/2008/05/java-single-application-instance.html

只需查看代码链接 在这里查看原始来源从谷歌获取 http://www.rbgrn.net/blog/2008/05/java-single-application-instance.html

  1. By accessing file from local file system. This is also another method for doing the same thing. But it is not that much preferable because sometime when JVM crashes or due to some IO error occured then file is not deleted from hard disk. note:- Dont put your file (you can use any file) in C drive or where OS exist.
    Just see below for code
  1. 通过从本地文件系统访问文件。这也是做同样事情的另一种方法。但它并不是那么可取,因为有时当 JVM 崩溃或由于某些 IO 错误发生时,文件不会从硬盘中删除。注意:- 不要将您的文件(您可以使用任何文件)放在 C 驱动器或操作系统所在的位置。
    请参阅下面的代码
/*
* Program for setting single instance in JAVA
* Copyright 2009 @ yuvadeveloper
* Code By:- Prashant Chandrakar
*
*/
import java.net.ServerSocket;
import javax.swing.JOptionPane;
import javax.swing.JFrame;
import java.io.IOException;
import java.net.BindException;
class SingleInstance
{
  public static ServerSocket serverSocket;
  public static String errortype = "Access Error";
  public static String error = "Application already running.....";
  public static void main(String as[])
  {
    try
    {
        //creating object of server socket and bind to some port number serverSocket = new ServerSocket(15486);
        ////do not put common port number like 80 etc.
        ////Because they are already used by system
        JFrame jf = new JFrame();
        jf.setVisible(true);
        jf.setSize(200, 200);
     }
     catch (BindException exc)
     {
        JOptionPane.showMessageDialog(null, error, errortype, JOptionPane.ERROR_MESSAGE);
        System.exit(0);
     }
     catch (IOException exc)
     {
        JOptionPane.showMessageDialog(null, error, errortype, JOptionPane.ERROR_MESSAGE);
        System.exit(0);
     }
   }
}
  1. By using java sun.jvmstat package from tools.jar.
  1. 通过使用 tools.jar 中的 java sun.jvmstat 包。

Just see the link for code

只需查看代码链接

  1. By using Launch4j application. It is a third party tools for creating a EXE for your application. It is giving you a facility of creating single instance application. Just try it. It is perfect tool.
  1. 通过使用 Launch4j 应用程序。它是用于为您的应用程序创建 EXE 的第三方工具。它为您提供了创建单实例应用程序的便利。就试一试吧。它是完美的工具。

Just see the launch4j application doc http://launch4j.sourceforge.net/

只需查看 launch4j 应用程序文档 http://launch4j.sourceforge.net/

回答by jdev

The way it is often done in unix systems is to create a file, which is an atomic operation and then check whether the file could be created or not. If it was possible to create the file then that process has the lock and is allowed to run. If the file could not be created, someone else must have the lock and the instance terminates promptly. The code for such a thing

在unix系统中,通常的做法是创建一个文件,这是一个原子操作,然后检查文件是否可以创建。如果可以创建该文件,则该进程具有锁定并被允许运行。如果无法创建文件,则其他人必须拥有锁定,并且实例会立即终止。这种事情的代码

private boolean lock()
 {
   try
    {
        final File file=new File("bpmdj.lock");
        if (file.createNewFile())
        {
            file.deleteOnExit();
            return true;
        }
        return false;
    }
    catch (IOException e)
    {
        return false;
    }
}

in the main of the app you then start with

在应用程序的主要部分,您然后开始

    if (!lock())
    {
        System.out.println("Cannot lock database. Check that no other instance of BpmDj is running and if so, delete the file bpmdj.lock");
        return;
    }

Of course, there are two caveats to be mentioned. First of all: if the app crashes hard, then the file will very likely not be deleted, resulting in some inconvenience for the user (he will need to remove the lockfile himself).

当然,有两点需要注意。首先:如果应用程序崩溃,那么文件很可能不会被删除,从而给用户带来一些不便(他需要自己删除锁定文件)。

Secondly: the java documentationstates the following:

其次:java文档说明如下:

createNewFileatomically creates a new, empty file ... if and only if a file with this name does not yet exist. The check for the existence of the file and the creation of the file if it does not exist are a single operation that is atomic with respect to all other filesystem activities that might affect the file. Note: this method should not be used for file-locking, as the resulting protocol cannot be made to work reliably. The FileLock facility should be used instead.

createNewFile原子地创建一个新的空文件...当且仅当具有此名称的文件尚不存在时。检查文件是否存在以及如果文件不存在则创建文件是单个操作,对于可能影响文件的所有其他文件系统活动而言是原子操作。注意:此方法不应用于文件锁定,因为无法使生成的协议可靠地工作。应该改用 FileLock 工具。

Especially the last note is interesting because in this case we don't really use it for file-locking, merely to check that no other instances of the same application are present. Yet I'm a bit curious to understand why they write that the 'resulting protocol cannot be made to work reliable'

特别是最后一个注释很有趣,因为在这种情况下,我们并没有真正将它用于文件锁定,只是为了检查是否存在同一应用程序的其他实例。然而,我有点好奇他们为什么写“结果协议不能可靠地工作”

回答by kolobok

You can use JUnique library. It provides support for running single-instance java application and is open-source. It's based on file locks, but use also random port to send/receive messages from other running java instances.

您可以使用 JUnique 库。它提供对运行单实例 java 应用程序的支持,并且是开源的。它基于文件锁,但也使用随机端口来发送/接收来自其他正在运行的 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 应用程序中的完整回答