Java程序单实例运行的简单实现

目录
  • 需求
  • 实现方式
  • 代码实现
    • 第一种实现(端口控制)
    • 第二种实现(文件锁)
    • 第三种方式(端口+文件锁)

需求

最近做了个java项目,功能完成后打包安装了,发现可以点开多个实例,因为桌面显示托盘,所以点一次就会出现一个托盘,并且系统也多了好几个javaw进程,这样的话就不能保证程序的健壮性了,所以需要做一个判断让程序只运行一个实例。

实现方式

Java没有提供这样的机制。从操作系统的观点来看,一个启动的Java Application仅仅是一个JVM的运行实例。运行相同Application的两个实例,仅仅是运行两个无关的JVM。

只有让多个运行实例之间有一个既定的通讯机制就可以保证只有一个实例运行。

因为要考虑平台无关,java程序的实例控制不应该使用系统的内核对象来完成,那么我们就必须找到其它的、可以独享的资源。实际上,一台机器无论是在什么操作系统上,网络端口都是独享的,也就是说基于网络端口这个独享的原理,我们可以很方便地让我们的Java程序实现在内存里面只有一个运行实例这个功能,而且这个功能的实现是与平台无关的。

使用端口号控制的方式,先创建端口,运行的时候再判断端口是否被占用来判断是否启动新实例。

文件锁的方式,这种方式的用法在于运行程序的时候将文件上锁,然后判断这个文件是否被锁进而来判断是否要运行一个新实例。

使用端口号+文件的方式,这种方式的用法在于启动的时候创建一个文件,关闭的时候删掉这个文件,当然仅仅这么一个操作不能起到上述要求的,如果非法关闭的话,文件还存在就不能满足要求,只能是再加上一个端口的控制,即当端口被占用并且文件存在的情况下就停止运行新实例,否则启动一个实例,经试验这种方式可以得到满足。

代码实现

第一种实现(端口控制)

//方案:使用java.net.ServerSocket
//问题:打开服务端口可能会受到防火墙的影响;可能和别的端口冲突。
import java.io.*;
import java.net.*;
public class OneInstance_2
{
    private static ServerSocket listenerSocket;
    public static void main(String[] args)
    {
        try
        {
            listenerSocket = new ServerSocket(2004);
            //At this point, no other socket may listen on port 2004.
        }
        catch(java.net.BindException e)
        {
            System.err.println("A previous instance is already running....");
            System.exit(1);
        }
        catch(final IOException e)     // an unexpected exception occurred
        {
            System.exit(1);
        }
        // Do some work here.....
    }
}

第二种实现(文件锁)

/*方案:使用Java的加锁文件机制,idea相当简单,让运行实例通过java.nio.channels.FileLock获得一个"well-known"文件的互斥锁。*/
//存在的问题:平台相关
import java.io.*;
import java.nio.channels.*;
public class OneInstance_1 {
  public static void main(String[] args) throws Exception {
    FileLock lck = new FileOutputStream("C:\\flagFile").getChannel().tryLock();
    if(lck == null) {
      System.out.println("A previous instance is already running....");
      System.exit(1);
    }
    System.out.println("This is the first instance of this program...");
    // Do some work here.....
  }
}
//方案3:使用File.createNewFile() and File.deleteOnExit()
//问题:文件可能因为某些原因不能被删除,即使利用Runtime.addShutdownHook()也有可能产生这种情况。
import java.io.*;
public class OneInstance_3
{
    public static void main(String[] args) throws Exception
    {
        File flagFile = new File("C:\\flagFile");
        if(false == flagFile.createNewFile())
        {
            System.out.println("A previous instance is already running....");
            System.exit(1);
        }
        flagFile.deleteOnExit();
        System.out.println("This is the first instance of this program...");    // Do some work here.....
    }
}

第三种方式(端口+文件锁)

public static void main(String[] args) throws IOException
{
    //创建lock.java文件
    String filePath = new File("IDRCallDll").getAbsolutePath().substring(0,
            new File("IDRCallDll").getAbsolutePath().lastIndexOf("\\"));
    File getFile = new File(filePath + "\\" + "lock.java");
    System.out.println(getFile.getPath());

    //判断端口是否被占用
    boolean flag = isLoclePortUsing(20520);
    System.out.println(flag);

    //如果文件存在并且端口被占用则退出
    if (getFile.exists() && flag)
    {

        new MyTray().showDialog();
        System.exit(0);
    }

    try
    {
        Socket sock = new Socket("127.0.0.1", 20520);// 创建socket,连接20520端口
    }
    catch (Exception e)
    {
        System.out.println("端口被占用!");
    }

    final Class<?> clazz = (Class<?>) JavaCall.class;
    final boolean isWindows = System.getProperty("os.name").contains(
                                  "Windows");
    final List<String> args1 = new ArrayList<String>();
    args1.add(isWindows ? "javaw" : "java");
    args1.add("-Xmx" + 128 + "M");
    args1.add("-cp");
     args1.add(System.getProperty("java.class.path"));
    args1.add("-Djava.library.path="
              + System.getProperty("java.library.path"));
    args1.add(clazz.getCanonicalName());
    // logger.info("start " + args1.toString());
    final ProcessBuilder pb = new ProcessBuilder(args1);
    pb.redirectErrorStream(true);
    try
    {
        /**
        * 读身份证信息程序
        */

        pb.start();
    }
    catch (Exception e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }  

    RandomAccessFile r = new RandomAccessFile(
        filePath + "\\" + "lock.java", "rws");
    FileChannel temp = r.getChannel();
    FileLock fl = temp.lock();
}

/**
* 判断端口是否被占用
* @param port
* @return
*/

public static boolean isLoclePortUsing(int port)
{
    boolean flag = true;
    try
    {
        flag = isPortUsing("127.0.0.1", port);
    }
    catch (Exception e)
    {
    }
    return flag;
}

public static boolean isPortUsing(String host, int port) throws UnknownHostException
{
    boolean flag = false;
    InetAddress theAddress = InetAddress.getByName(host);
    System.out.println(theAddress);

    try
    {
        ServerSocket socket = new ServerSocket(port);
        flag = true;
    }
    catch (IOException e)
    {
        System.out.println("beizhanyong");
    }
    return flag;
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • java布局管理之CardLayout简单实例

    本文实例为大家分享了java布局管理之CardLayout的具体代码,供大家参考,具体内容如下 import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swi

  • java使用jdbc连接数据库简单实例

    本文为大家分享了java使用jdbc连接数据库的具体代码,供大家参考,具体内容如下 package com.tr.jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.apache.commons.dbcp2.BasicDataSource; public class CollectionFac

  • java GUI实现学生图书管理简单实例

    本文实例为大家分享了java GUI学生图书管理的具体代码,供大家参考,具体内容如下 - mysql数据库建表: 1.book表 2.bs借书记录表 3.std学生表 4.dl登录用户表 . - 列表内容 1.databd.java //程序入口及登录验证 import java.awt.*; import java.awt.event.*; import java.sql.*; import javax.swing.*; import javax.swing.border.EmptyBorde

  • 微信公众号开发之设置自定义菜单实例代码【java版】

    本实例是为了实现在管理后台实现微信菜单的添加删除管理. 1.首先我们需要新建一个数据库表用于存放menu菜单项 可包含的字段有id.父类id.name.排序.是否显示.类型(view.click).链接.adddate 注意后台存menu菜单数据时,parentId=-1为一级菜单,或parendId为一级菜单的id作为该一级菜单下的二级菜单 2.在设置菜单时需要向微信接口传menuJson字符串,所以要先拼接字符串,后台定义一个creatMenu() public bool creatMenu

  • Java实现跳跃表(skiplist)的简单实例

    跳跃链表是一种随机化数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间),并且对并发算法友好. 基本上,跳跃列表是对有序的链表增加上附加的前进链接,增加是以随机化的方式进行的,所以在列表中的查找可以快速的跳过部分列表(因此得名).所有操作都以对数随机化的时间进行. 实现原理: 跳跃表的结构是:假如底层有10个节点, 那么底层的上一层理论上就有5个节点,再上一层理论上就有2个或3个节点,再上一层理论上就有1个节点.所以从这里可以看出每一层的节点个数为其下

  • java 实现MD5加密算法的简单实例

    java 实现MD5加密算法的简单实例 实现代码: import java.security.NoSuchAlgorithmException; public class MD5HashUtil { private MessageDigest md = null; private static MD5HashUtil md5 = null; private static final char[] hexChars ={'0','1','2','3','4','5','6','7','8','9'

  • java 实现音乐播放器的简单实例

    java 实现音乐播放器的简单实例 实现效果图: 代码如下 package cn.hncu.games; import java.applet.Applet; import java.applet.AudioClip; import java.awt.Color; import java.awt.Font; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.Mou

  • Java注解处理器简单实例

    如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了.使用注解的过程中,很重要的一部分就是创建于使用注解处理器.JavaSE5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器. 注解处理器类库(java.lang.reflect.AnnotatedElement): Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口.除此之外,Java在java.lang.reflect包下新增了AnnotatedElement

  • Java程序单实例运行的简单实现

    目录 需求 实现方式 代码实现 第一种实现(端口控制) 第二种实现(文件锁) 第三种方式(端口+文件锁) 需求 最近做了个java项目,功能完成后打包安装了,发现可以点开多个实例,因为桌面显示托盘,所以点一次就会出现一个托盘,并且系统也多了好几个javaw进程,这样的话就不能保证程序的健壮性了,所以需要做一个判断让程序只运行一个实例. 实现方式 Java没有提供这样的机制.从操作系统的观点来看,一个启动的Java Application仅仅是一个JVM的运行实例.运行相同Application的

  • C#使用Mutex简单实现程序单实例运行的方法

    本文实例讲述了C#使用Mutex简单实现程序单实例运行的方法.分享给大家供大家参考.具体如下: [STAThread] static void Main() { bool isAppRunning = false; System.Threading.Mutex mutex = new System.Threading.Mutex(true,System.Diagnostics.Process.GetCurrentProcess().ProcessName,out isAppRunning); i

  • 使用mutex实现应用程序单实例运行代码分享

    System.Threading.Mutex :同步基元,它只向一个线程授予对共享资源的独占访问权.实现原理: 在程序启动时,请求一个互斥体,如果能获取对指定互斥的访问权,就继续运行程序,否则就退出程序.测试代码: 复制代码 代码如下: class Test     {         /// <summary>         /// 应用程序的主入口点.         /// </summary>          [STAThread]         static voi

  • Java操作另一个Java程序使其重启的简单实现

    大概思路: 写两个程序,一个负责重启的程序,一个是待重启的程序,在这里为了区分我们假设负责重启的那个程序叫A,待重启的程序叫B,他们都是线程,还要搭配数据库,他是两个程序的桥梁,通过设置信号量进行判断程序状态(不妨设置信号量为Flag),我是这么设置的,0:表示程序正在运行中,1:表示程序需要重启,正准备做关闭自己的操作(只针对待重启的程序B),2:表示B程序已经把自己给关闭了,需要A程序把B程序启动. 实现步骤: A程序:写一个线程进行读信号量Flag,当Flag为2的时候就把B程序启动 B程

  • Perl中使用File::Lockfile确保脚本单实例运行

    用Perl写了一些监控脚本,放在crontab中调度执行.有时候会发现一个脚本运行时间过长,会同时跑起多个实例,因此有必要为脚本加上控制,只运行一个实例. 最简单自然的想法,在脚本中检查并创建一个空的lock文件,脚本结束时再删除.通过判断文件是否存在的方式来判断脚本是否已经运行.不过这样做有个bug,如果脚本运行过程中异常终止,lock文件没有正常删除,就会导致脚本无法再运行. 空的lock文件不行,那么考虑在lock文件中加入一点内容,比如进程的PID号,然后通过检查该PID号的进程是否还在

  • linux让程序开机自动运行最简单的方法

    搜集了很多个,均以失败告终,最后发现,这个办法好,而且不影响使用,对于安装了xampp的系统来说,更加重要,希望大家赞同.方法如下: 在/etc/init.d目录中新建文件,并设置文件权限为可运行.名字随便取,我起的名字是:xamppv.内容如下,尤其是前几个带有#号的行,不可少,不可改. #!/bin/sh ### BEGIN INIT INFO # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 ### END INIT INFO PATH=/usr

  • Java程序执行时间的2种简单方法

    第一种是以毫秒为单位计算的.  Java代码  //伪代码   复制代码 代码如下: long startTime=System.currentTimeMillis();   //获取开始时间 doSomeThing();  //测试的代码段 long endTime=System.currentTimeMillis(); //获取结束时间 System.out.println("程序运行时间: "+(end-start)+"ms"); 第二种是以纳秒为单位计算的.

  • 浅谈Java程序运行机制及错误分析

    JVM(Java虚拟机)一种用于计算设备的规范,可用不同的方式(软件或硬件)加以实现.编译虚拟机的指令集与编译微处理器的指令集非常类似.Java虚拟机包括一套字节码指令集.一组寄存器.一个栈.一个垃圾回收堆和一个存储方法域. Java虚拟机(JVM)是可运行Java代码的假想计算机.只要根据JVM规格描述将解释器移植到特定的计算机上,就能保证经过编译的任何Java代码能够在该系统上运行. 1.为什么要使用Java虚拟机 Java语言的一个非常重要的特点就是与平台的无关性.而使用Java虚拟机是实

  • hadoop运行java程序(jar包)并运行时动态指定参数

    1)首先启动hadoop2个进程,进入hadoop/sbin目录下,依次启动如下命令 [root@node02 sbin]# pwd /usr/server/hadoop/hadoop-2.7.0/sbin sh start-dfs.sh sh start-yarn.sh jps 2)通过jps查看是否正确启动,确保启动如下6个程序 [root@node02 sbin]# jps 10096 DataNode 6952 NodeManager 9962 NameNode 10269 Second

  • docker启动Java程序的方法步骤

    创建一个简单的Spring boot web项目 idea工具创建Spring boot web项目,因为是测试,一直next就行. 写一个test API,用来访问,服务端口号可以不用改,我本地改成8701. 程序启动,发现程序不是默认的8080端口了,访问:http://localhost:8701/v1/hello 以上一个简单web项目建好了,下面我们通过docker来运行这个demo项目 第一步,你需要安装docker(这里不做详细步骤). 第二步,我们需要一个有java环境docke

随机推荐