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

时间:2013-09-29 19:24:13

标签: java

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

解决方案基于:

  • 通过打开套接字:打开套接字连接。
  • 基于文件锁定:创建临时文件并保持锁定。并在JVM关闭时添加一个关闭钩子来解锁该文件。

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

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

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

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

5 个答案:

答案 0 :(得分:3)

您可以使用ManagementFactory对象。来自here: -

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.");  

  }  
  });  
  }
  

getMonitoredVMs(int processPid)方法接收为参数   当前应用程序PID,并捕获被调用的应用程序名称   例如,从命令行启动应用程序   c:\ java \ app \ test.jar路径,然后是值变量   “C:\ java的\程序\ teste.jar”。这样,我们将只捕获应用程序名称   在下面的代码的第17行。之后,我们搜索JVM   如果我们找到它和应用程序,则使用相同名称的antoher进程   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;  
   }

同时检查 Using the SingleInstanceService Service

  

javax.jnlp.SingleInstanceService 提供了一组方法   应用程序将自己注册为单例,并进行注册   用于处理从不同实例传入的参数的侦听器   申请。

答案 1 :(得分:1)

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

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

可以在here

中找到Windows中命名管道的示例

答案 2 :(得分:1)

嗨,有很多方法可以做到,只需访问此页面即可。 我只是复制粘贴。 也查看此主题 [计算器] [1]

java世界中最受欢迎的问题之一是如何制作java 应用程序作为单个实例。

我谷歌并发现了很多技巧。 我在这里发布了一些流行的技巧。 如果您有问题,请继续联系......

  1. 通过捕获端口或通过ServerSocket(短代码)。 在这个方法中,我们创建了一个java.net.ServerSocket类的对象。 通过传递端口号,我们在第一个实例时被捕获 如果发生另一个实例,它会抛出一个绑定异常和 您可以跟踪系统上正在运行的任何实例。
  2. 只需查看代码链接即可 http://yuvadevelopers.dmon.com/java_examples/Single_Instance_small.htm

    1. 通过捕获端口或通过ServerSocket(大代码)。 它与第一种方法相同,但谷歌我得到了这个大代码 不同的选项只需通过代码。
    2. 只需查看代码链接即可 在这里查看原始来源获取谷歌 http://www.rbgrn.net/blog/2008/05/java-single-application-instance.html

      1. 从本地文件系统访问文件。 这也是做同样事情的另一种方法。 但它并不是那么好,因为有时JVM 崩溃或由于发生一些IO错误,文件不会被删除 从硬盘。 注意: - 不要把你的文件(你可以使用任何文件)放在C盘或OS存在的地方 请参阅下面的代码
      2. /*
        * 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. 使用tools.jar中的java sun.jvmstat包。
        2. 只需查看代码链接

          即可
          1. 使用Launch4j应用程序。 它是为您的应用程序创建EXE的第三方工具。 它为您提供了创建单实例应用程序的功能。 就试一试吧。这是完美的工具。
          2. 只需查看launch4j应用程序文档即可 http://launch4j.sourceforge.net/

答案 3 :(得分:1)

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

在应用程序的主要部分,然后以

开头
    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;
    }

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

其次:java documentation声明如下:

  

createNewFile原子地创建一个新的空文件... if和only   如果具有此名称的文件尚不存在。检查   存在文件和文件的创建,如果没有   存在是一个相对于所有其他操作是原子的单个操作   可能影响文件的文件系统活动。注意:这个方法   不应该用于文件锁定,因为生成的协议不能   使可靠的工作。应该使用FileLock工具   代替。

特别是最后一个注释很有意思,因为在这种情况下我们并不真正使用它来进行文件锁定,只是为了检查是否存在同一个应用程序的其他实例。然而,我有点好奇理解为什么他们写道'由此产生的协议无法使其工作可靠'

答案 4 :(得分:0)

您可以使用JUnique库。它为运行单实例java应用程序提供支持,并且是开源的。它基于文件锁,但也使用随机端口从其他正在运行的java实例发送/接收消息。

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

另见How to implement a single instance Java application?

的完整答案