让Java程序只运行一个实例

让Java程序只运行一个实例,第1张

一个程序可以在内存里面存在多个运行实例 比如 你可以打开多个微软的Word程序 但是 有些时候我们需要控制程序运行的实例只有一个 也就是说 该程序同一时刻在内存里面运行的只有一个实例 这样当这个程序在内存中已经存在一个运行实例而用户又再次运行了该程序的时候 有两种结果 第一种结果是结束目前的运行实例 打开新运行的实例 第二种就是让新运行的实例退出 原有的运行实例继续运行 原理 因为任何时候只有一个实例 所以在实现这种功能的时候必须借助只能被独享的资源 如果我们的程序是基于某个平台的 那么就可以借助 *** 作系统的内核对象来完成 比如Windows *** 作系统就提供了CreateMutex这个API来创建一个独享的内核对象 但是因为要考虑平台无关 Java程序的实例控制不应该使用系统的内核对象来完成 那么我们就必须找到其它的 可以独享的资源 实际上 一台机器无论是在什么 *** 作系统上 网络端口都是独享的 也就是说基于网络端口这个独享的原理 我们可以很方便地让我们的Java程序实现在内存里面只有一个运行实例这个功能 而且这个功能的实现是与平台无关的 实现 我们先来看看第一种情况是如何实现的 也就是说如果系统中已经存在运行实例的话 那么结束原有的运行实例 让新实例运行 这个实现实例控制的Java类也是一个线程 具体的实现如下 import ;public class InstanceControl extends Thread { public void run() { try{  Socket sock = new Socket( ); //创建socket 连接 端口} catch (Exception e) {} try{  ServerSocket server = new ServerSocket( );//创建socket 在 端口监听  server accept(); //等待连接  server close(); //有连接到来 也就是说有新的实例  System exit( ); //这个实例退出 }catch (Exception e) {  e printStackTrace(); } }} 下面这个Java程序的程序入口是没有实例控制功能的 public class ProgramMain { public static void main(String argv[]) { mainFrame frame = new mainFrame(); }} 现在想加入实例控制 只需要添加两行代码 添加后代码如下所示 public class ProgramMain { public static void main(String argv[]) { InstanceControl ic = new InstanceControl(); ic start(); mainFrame frame = new mainFrame(); }} 在这个基础上 要实现第二种情况 也就是已经有实例运行的情况下 新的实例退出 保持原有的运行实例 就只需要一点小的改动了 具体的实现如下 import ;public class InstanceControl extends Thread { public void run() { try{  Socket sock = new Socket( );//创建socket 连接 端口  System exit( ); //连接成功 说明有实例存在 则退出 }catch (Exception e) {} try{  ServerSocket server = new ServerSocket( );//创建socket 连接 端口  while (true)  {server accept(); //接受连接请求  } }catch (Exception e) {  e printStackTrace(); } }} 这个类的使用方法和第一种情况的那个类是一样的 只需要在原有的代码上加入两行代码即可 InstanceControl ic = new InstanceControl();ic start(); 扩展 上面的程序也许有一个小bug 就是如果程序在开始运行时ServerSocket监听的端口已经被其它程序占用 那么程序的运行就会受到影响 所以程序的端口应该尽量取得大一些 在这种情况下其它程序占用这个程序使用的端口的概率是可以忽略不计的 同时 还可以做两种扩展 第一种是把端口写在配置文件中 可通过读配置文件得到端口 这样就能够在其它程序占用目前端口的情况下改变这个程序使用的端口 还有一种是在运行的时候用两个InstanceControl类分别在两个端口监听 只要有一个InstanceControl类得到连接就做出响应 这样两个端口都被其它程序占用的概率就更加的微乎其微了 lishixinzhi/Article/program/Java/Javascript/201311/25374

;   FTP是Internet 上用来传送文件的协议 在Internet上通过FTP 服务器可以进行文件的上传(Upload)或下载(Download) FTP是实时联机服务 在使用它之前必须是具有该服务的一个用户(用户名和口令) 工作时客户端必须先登录到作为服务器一方的计算机上 用户登录后可以进行文件搜索和文件传送等有关 *** 作 如改变当前工作目录 列文件目录 设置传输参数及传送文件等 使用FTP可以传送所有类型的文件 如文本文件 二进制可执行文件 图象文件 声音文件和数据压缩文件等     FTP 命令    FTP 的主要 *** 作都是基于各种命令基础之上的 常用的命令有     ◆ 设置传输模式 它包括ASCⅡ(文本) 和BINARY 二进制模式     ◆ 目录 *** 作 改变或显示远程计算机的当前目录(cd dir/ls 命令)     ◆ 连接 *** 作 open命令用于建立同远程计算机的连接 close命令用于关闭连接     ◆ 发送 *** 作 put命令用于传送文件到远程计算机 mput 命令用于传送多个文件到远程计算机     ◆ 获取 *** 作 get命令用于接收一个文件 mget命令用于接收多个文件     编程思路    根据FTP的工作原理 在主函数中建立一个服务器套接字端口 等待客户端请求 一旦客户端请求被接受 服务器程序就建立一个服务器分线程 处理客户端的命令 如果客户端需要和服务器端进行文件的传输 则建立一个新的套接字连接来完成文件的 *** 作

    编程技巧说明    主函数设计    在主函数中 完成服务器端口的侦听和服务线程的创建 我们利用一个静态字符串变量initDir 来保存服务器线程运行时所在的工作目录 服务器的初始工作目录是由程序运行时用户输入的 缺省为C盘的根目录     具体的代码如下     public class ftpServer extends Thread{    private Socket socketClient;    private int counter;    private static String initDir;    public static void main(String[] args){    if(args length != ) {    initDir = args[ ];    }else{ initDir = c: ;}    int i = ;    try{    System out println( ftp server started! )     //监听 号端口    ServerSocket s = new ServerSocket( )     for( ){    //接受客户端请求    Socket ining = s accept()     //创建服务线程    new ftpServer(ining i) start()     i++;    }    }catch(Exception e){}    }    线程类的设计    线程类的主要设计都是在run()方法中实现 用run()方法得到客户端的套接字信息 根据套接字得到输入流和输出流 向客户端发送欢迎信息     FTP命令的处理    ( ) 访问控制命令    ◆ user name(user) 和 password (pass) 命令处理代码如下     if(str startsWith( USER )){    user = str substring( )     user = user trim()     out println( Password ) }    if(str startsWith( PASS ))    out println( User +user+ logged in )     User 命令和 Password 命令分别用来提交客户端用户输入的用户名和口令     ◆ CWD (CHANGE WORKING DIRECTORY) 命令处理代码如下     if(str startsWith( CWD )){    String str = str substring( )     dir = dir+ / +str trim()     out println( CWD mand succesful ) }    该命令改变工作目录到用户指定的目录     ◆ CDUP (CHANGE TO PARENT DIRECTORY) 命令处理代码如下     if(str startsWith( CDUP )){    int n = dir lastIndexOf( / )     dir = dir substring( n)     out println( CWD mand succesful ) }    该命令改变当前目录为上一层目录     ◆ QUIT命令处理代码如下     if(str startsWith( QUIT )) {    out println( GOOD BYE )     done = true;}    该命令退出及关闭与服务器的连接 输出GOOD BYE

    ( ) 传输参数命令    ◆ Port命令处理代码如下     if(str startsWith( PORT )) {    out println( PORT mand successful )     int i = str length() ;    int j = str lastIndexOf( )     int k = str lastIndexOf( j )     String str str ;str = ;    str = ;for(int l=k+ ;    lstr = str + str charAt(l) }    for(int l=j+ ;l<=i;l++){    str = str + str charAt(l) }    tempPort = Integer parseInt(str ) +Integer parseInt(str )     }    使用该命令时 客户端必须发送客户端用于接收数据的 位IP 地址和 位 的TCP 端口号 这些信息以 位为一组 使用十进制传输 中间用逗号隔开     ◆ TYPE命令处理代码如下     if(str startsWith( TYPE )){    out println( type set )     }    TYPE 命令用来完成类型设置     ( ) FTP 服务命令    ◆ RETR (RETEIEVE) 和 STORE (STORE)命令处理的代码     if(str startsWith( RETR )){    out println( Binary data connection )     str = str substring( )     str = str trim()     RandomAccessFile outFile = newRandomAccessFile(dir+ / +str r )     Socket tempSocket = new Socket(host tempPort)     OutputStream outSocket= tempSocket getOutputStream()     byte byteBuffer[]= new byte[ ];    int amount;    try{    while((amount = outFile read(byteBuffer)) != ){

    outSocket write(byteBuffer amount)     }    outSocket close()     out println( transfer plete )     outFile close()     tempSocket close() }    catch(IOException e){}    }    if(str startsWith( STOR )){    out println( Binary data connection )     str = str substring( )     str = str trim()     RandomAccessFile inFile = newRandomAccessFile(dir+ / +str rw )     Socket tempSocket = new Socket(host tempPort)     InputStream inSocket= tempSocket getInputStream()     byte byteBuffer[] = new byte[ ];    int amount;    try{    while((amount =inSocket read(byteBuffer) )!= ){    inFile write(byteBuffer amount) }    inSocket close()     out println( transfer plete )     inFile close()     tempSocket close() }    catch(IOException e)    {}    }    文件传输命令包括从服务器中获得文件RETR和向服务器中发送文件STOR 这两个命令的处理非常类似 处理RETR命令时 首先得到用户要获得的文件的名称 根据名称创建一个文件输入流 然后和客户端建立临时套接字连接 并得到一个输出流 随后 将文件输入流中的数据读出并借助于套接字输出流发送到客户端 传输完毕以后 关闭流和临时套接字     STOR 命令的处理也是同样的过程 只是方向正好相反     ◆ DELE (DELETE)命令处理代码如下     if(str startsWith( DELE )){    str = str substring( )     str = str trim()     File file = new File(dir str)     boolean del = file delete()     out println( delete mand successful )     }    DELE 命令用于删除服务器上的指定文件     ◆ LIST命令处理代码如下     if(str startsWith( LIST )) {    try{    out println( ASCII data )     Socket tempSocket = new Socket(host tempPort)     PrintWriter out = new PrintWriter(tempSocket getOutputStream() true)     File file = new File(dir)     String[]    dirStructure = new String[ ];    dirStructure= file list()     String strType= ;    for(int i= ;iif( dirStructure[i] indexOf( ) == ) {    strType = d ;}    else{    strType = ;}    out println(strType+dirStructure[i]) }    tempSocket close()     out println( transfer plete ) }    catch(IOException e)    {}    LIST 命令用于向客户端返回服务器中工作目录下的目录结构 包括文件和目录的列表 处理这个命令时 先创建一个临时的套接字向客户端发送目录信息 这个套接字的目的端口号缺省为 然后为当前工作目录创建File 对象 利用该对象的list()方法得到一个包含该目录下所有文件和子目录名称的字符串数组 然后根据名称中是否含有文件名中特有的 来区别目录和文件 最后 将得到的名称数组通过临时套接字发送到客户端 lishixinzhi/Article/program/Java/hx/201311/26847

Java程序完成以后 对于Windows *** 作系统 习惯总是想双击某个exe文件就可以直接运行程序 现我将一步一步的实现该过程 最终结果是:不用安装JRE环境 不用安装数据库 直接双击一个exe文件 就可以运行程序

下面我将以我最近写的一个程序作例子 进行该打包过程

该程序是使用了:Hibernate Spring Derby的Java GUI (JDK需要 版本 因该版本才有Derby) 我使用的是Eclipse进行开发

第一步:完成Java GUI程序

在Eclipse下 程序能够正常运行

第二步:准备清单文件(MANIFEST MF)

方法 :直接拷贝其它能运行的JAR文件中的MANIFEST MF进行修改

方法 :利用IDE工具 如Eclipse在生成JAR文件时自动生成

MANIFEST MF内容如下:

Manifest Version:

Main Class: manager ui MainFrame

Class Path: /lib/spring jar /lib/hibernate jar /lib/derby jar

SplashScreen Imager: manager/resources/images/splash jpg

说明:

Manifest Version - 指定清单文件的版本号

Main Class -指定程序运行的入口类 注意 类名后不要加class扩展名

Class Path -指定支持库的路径 指程序运行目录 即导出的JAR包所在目录 程序运行时依据Class Path项的设置路径来查找支持库 每一个支持库之间用空格隔开 在这里使用了hibernate spring derby

注意:

如果Java应用程序用到了一些Eclipse包 那么就必须将这些包也复制到程序运行目录 设置到Class Path 否则程序将无法运行 引用了一些外部组件也需要如此设置 如hibernate spring derby

除了入口类的包名和类名之外 其他设置项都不分大小写 比如 Class Path写成class path或CLASS PATH也可以 swt jar写成SWT JAR也行

类名后不要加class扩展名

每一行的 : 后都有一个空格 如Class Path:<空格> /lib/spring jar

Class Path中 引入的JAR文件每行不得超过 个 否则会报错 但在让人意外的是每行只有前 个有效 故将所有同类型的JAR文件做成一个JAR文件 如我这里的spring jar hibernate jar derby jar都是同类JAR文件的组合

第三步:修改spring配置文件

在spring的 sessionFactory bean的配置中则要这样写(与程序中ClassPathXmlApplicationContext对应)

<property name= mappingLocations >

<list>

<value>manager/entity/MyFile hbm xml</value>

<value>manager/entity/FileType hbm xml</value>

</list>

</property>

第四步:使用Eclipse生成JAR文件

右键单击项目名 在d出菜单中选择 Export 在d出的如下图所示的对话框中 选择 JAR文件 单击 Next

将右边不需要的文件都取消勾选 在 选择导出目标(Select the export destination) 项文本框中设置JAR包的输出路径和包名(可以任意取名)为 D:\manager\manager jar 接受其他的默认设置不变 单击 Next

附注 左边虽然选择了src目录 但源文件并不会导出到包中 除非勾选了 导出Java源代码文件和资源(Export java source files and resources) 项

接受默认设置不变 单击 Next

这一步较关键 如下图所示 选择 从工作空间中使用现有清单 项 将创建的清单文件输入 也可以通过旁边的 浏览 按钮来选择清单文件 输入清单文件后 单击 Finish Eclipse开始将项目打包

附:这里也可以选择Generate the manifest file 但生成的清单文件MAINFEST MF需要修改

注意:清单文件MAINFEST MF一定要按第二步那样设置

生成的JAR文件目录如下:

第五步:生成运行manager jar的批处理文件manager bat(该步可以不要)

在manager目录下创建一个批处理程序manager bat(名字任取 扩展名必须是bat) 其内容仅一句语句 如下

javaw jar manager jar

说明

javaw对应c:\jdk\jre\bin\javaw exe文件 如果windows提示命令未发现 则需要将c:\jdk\jre\bin路径加入到windows环境变量path中

在运行程序的时候有一个讨厌的黑色命令行窗口 要去掉它 可以将run bat内容更改如下 start javaw jar manager jar start是指调用了windows的 运行 命令

在后边加一个pause就可以让你看到具体哪里出错了 如:

start javaw jar manager jar

pause

双击manager bat 就可以运行该JAR文件了

第六步:让电脑不必安装JRE环境 也能运行

通常运行Java程序有个前提条件 用户电脑必须先安装JRE环境 虽然安装JRE环境非常简单 但毕竟多了一步 算是有一点点的瑕疵 这里给出一个不必让用户安装JRE环境的方法 其实现步骤如下

( )将原JDK中的 jre 目录复制到 D:\manager\java 目录下(java也可换成其他名称)

( )将JDK和JRE从本机卸载掉 这样表示本机没有安装JAVA运行环境

( )修改批处理文件manager bat中的命令为 start java\jre\bin\javaw jar manager jar 仅仅是在javaw前加上了一个相对应路径

双击manager bat即可在不安装JRE环境的电脑运行此Java应用程序

第七步:抛弃批处理文件( bat) 生成exe文件

用批处理文件运行程序似乎不够专业 虽然它足以完成运行任务 但习惯就象一种毒药一旦染上就很难摆脱它的影响 Windows统治下的人们早已经习惯运行扩展名是EXE的程序 用 bat他们就会感觉别扭

我们可以用一个叫JavaLauncher的免费小程序来代替批处理文件去运行Java程序 JavaLauncher的下载网址是

下载下来的文件是一个名JavaLauncher zip的压缩包 解压后的目录结构如下所示

JavaLauncher zip目录结构

source        目录包含了JavaLauncher的源程序 是用C语言写的

changes txt   是新版的修改说明

launch exe    是主程序

launcher cfg  是配置文件

readme txt    是一些说明和示例

我们只需要launch exe launcher cfg两个文件 将这两个文件复制到打包文件所在的目录 launcher cfg是一个仅三行内容的文本文件 将它修改如下

\java\jre\bin\javaw exe

jar manager jar

第一行设置指向JAR包managger jar的目录 由于launch exe和myswt jar同在一个目录 所以用 即当前目录

第二行设置指向jre\bin\javaw exe的路径 在上面已将jre目录复制到了java子目录中

配置好launcher cfg后 双击launch exe即可运行java应用程序

如果仔佃研究eclipse的启动方式 发现eclipse和JavaLauncher的原理一样 eclipse exe相当于launch exe startup jar相当于manager jar 只不过eclipse exe不象launch exe要具有通用性 所以它没有 cfg这样的配置文件 而是将启动信息固化在eclipse exe中

第八步:美化图标

launch exe文件的图标太单调了 让我们给它换个好看点的 换程序的图标需要用到一个免费的软件 Resource Hacker

第九步:最后的打包

发送给用户之前 通常要用WinZip或WinRAR将所有的文件全部打成一个压缩包 然后用户得到这个压缩包后 将其解压缩后即可运行程序 Eclipse软件就是这种方式

lishixinzhi/Article/program/Java/hx/201311/25574

以上就是关于让Java程序只运行一个实例全部的内容,包括:让Java程序只运行一个实例、使用Java实现FTP服务器、Java程序打成Jar包全过程等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/10130820.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-05
下一篇2023-05-05

发表评论

登录后才能评论

评论列表(0条)

    保存