有没有办法只启动一个程序 程序启动之来之后开一个socket端口,每次启动之前连接一下这个端口,如果这个端口有回应,则直接System.exit 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 引用网上的文章http://bbs.51cto.com/archiver/tid-418151.html防止JAVA程序重复启动的一个另类解决办法 我们项目中有一个后台任务处理程序,是java开发application,用以处理网站提交的一些批量数据文件,因为这些数据文件数据量一般都比较大,所以写了这个批量处理程序,用以异步处理这些批量数据文件。这个程序设计成插件式的,处理各种不同数据文件的功能单独作为一个插件,然后使用Spring 来粘合各个组件,这样就可以很方便地对该程序进行扩展。 今天客户提出一个要求:需要控制这个程序在同一主机上只能启动一个实例。 为了实现客户要求,我首先想到就是在数据库中建一张表,程序启动时往该表中写入一个标志,等程序结束时再删除标志。但这种方式存在一个问题就是,如果程序是非正常停止或被杀进程,那么这个标志就不可能被清除,那下一次启动就会误判为重复启动;另外,如果用数据库来记录启动标志的话,还把该程序跟数据库紧密 耦合起来,感觉很别扭。 排除了第一种方案之后,我以想到了用文件来保存启动标志(好象一些大型的程序,诸如weblogic好象就是采用在文件中记录启动标志方 式来控制重复启动的)。客流量然这种方式不需要与数据库耦合在一起,但也存在程序异常中止而无法清除启动标志的问题,所以这个方案也被枪毙了。 我想到的第三种方案就是在JAVA中调用操作系统的查看系统进程的方式来取得系统进程,然后再检测系统进程有特殊的进程标志来判断是否重 复启动。但这种方式一是看起来很别扭,再者就是Window和 *nix系统中查看系统进程的命令不一样,分成几种情况来处理,无端地增加了程序的复杂性,也不可取。 能不能在内存中记录一个启动标志呢?理论上这应该是不可行的,因为跨JVM来相互操作内存数据是不可能。我在网上搜了一下,也没找到相关的例子。 那能不能占用一点系统共享资源,来换取我们的目标呢?比较容易想到的系统资源并且不能重复使用的资源就是端口。我尝试采用如下方案:在程序中指定一个不常用的端口(比如:12345),在程序启动时,就指定的端口启动一个ServerSocket,这个Socket只是为了占用这个端口,不接受任何网络连接。如果试图启动第二个实例时,程序在该指定端口启动ServerSocket时就会抛异常,这时我们就可以认为系统已经启动过了,然后打印提示并直接退出程序即可。这种方式在理论上分析应该可以的,我开始动手修改程序。程序修改如下:java 代码package cn.com.pansky.xmdswz.application.scheduler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.SchedulerException; import org.quartz.impl.StdScheduler; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.com.pansky.xmdswz.system.cache.CachedTableMgr; import cn.com.pansky.xmdswz.system.config.SystemConfig; import cn.com.pansky.xmdswz.utility.DateUtil; import org.quartz.JobDetail; import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean; import java.net.ServerSocket; import java.io.*; /** * Title: XXXXXXX * Description: XXXXXXXXXXXX * Copyright: Copyright (c) 2006 * Company: www.pansky.com.cn * * @author Sheng Youfu * @version 1.0 */ public class Scheduler { private static Log log = LogFactory.getLog(Scheduler.class); private static ServerSocket srvSocket = null; //服务线程,用以控制服务器只启动一个实例 private static final int srvPort = 12345; //控制启动唯一实例的端口号,这个端口如果保存在配置文件中会更灵活 /** * 定时任务配置文件 */ private static String CONFIG_FILE = "cn/com/pansky/xmdswz/application/scheduler/Scheduling-bean.xml"; public Scheduler() { //检测系统是否只启动一个实例 checkSingleInstance(); //下面读取Spring的配置文件 SystemConfig cfg = new SystemConfig(); String config = cfg.parseParam("SCHEDULER.CONFIG_FILE", false); if(config!=null && !"".equals( config.trim())) CONFIG_FILE = config; log.debug("CONFIG_FILE: "+CONFIG_FILE); } /** * 主函数 * @param args String[] * @throws Exception */ public static void main(String[] args) throws Exception{ Scheduler sch = new Scheduler(); sch.execute(); } /** * 运行定时任务 */ public void execute() { ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {CONFIG_FILE}); BeanFactory factory = (BeanFactory) appContext; /** * 装载任务调度 */ StdScheduler scheduler = (StdScheduler) factory.getBean("schedulerFactoryBean"); //先暂停所有任务,等待装载缓存代码表 try { scheduler.pauseAll(); } catch (SchedulerException ex) { log.error("",ex); } /** * 装载缓存代码表 */ CachedTableMgr cachedtableMgr = (CachedTableMgr) factory.getBean("cachedTableMgr"); try { cachedtableMgr.loadCodeTable(); } catch (Exception ex) { log.fatal("Load cached table failed. System will exit.", ex); System.exit(0); } //重新恢复所有任务 try { scheduler.resumeAll(); } catch (SchedulerException ex) { log.error("",ex); } } /** * 检测系统是否只启动了一个实例 */ protected void checkSingleInstance() { try { srvSocket = new ServerSocket(srvPort); //启动一个ServerSocket,用以控制只启动一个实例 } catch (IOException ex) { if(ex.getMessage().indexOf("Address already in use: JVM_Bind")>=0) System.out.println("在一台主机上同时只能启动一个进程(Only one instance allowed)。"); log.fatal("", ex); System.exit(0); } } } 经过测试,程序能很好地满足我们的要求,问题解决。 我之所以称这种方式另类,是因为这种方式以牺牲一个端口的代价来达到我们的设计要求,采用这种方式的人应该不多。但我认为,只要我们牺牲的代价与我们的目标比较起来是在可接受的范围内,这种方式就是可取的,这与我们花钱增加内存来让程序运行更快在本质应该是相同的。 1,用socket可以...2,配置文件config记录也是可以的,改写文件的时候lock锁定一下. Swing Hacks里的那个是重用已经启动的那个实例。可以处理用户输入。比如:javaw -jar SomeJar.jar args使用已经启动的实例来处理args 求高手帮忙写个算法 java中url格式转换 scjp中的一道题 怎么将输出流倒序输出? Java类数组初始化问题 问个简单的问题!! 大家看看 急求 :jsp 开发tomcat,sql server Hashtable怎么取得vlaue 即将是初学者的一个问题?100分相送---------------- 我是初学者! 请问那个中文网站可以下载JDK!详细路径! JLabel 中用HTML的问题 Java里有没有乘方运算符?
我们项目中有一个后台任务处理程序,是java开发application,用以处理网站提交的一些批量数据文件,因为这些数据文件数据量一
般都比较大,所以写了这个批量处理程序,用以异步处理这些批量数据文件。这个程序设计成插件式的,处理各种不同数据文件的功能单
独作为一个插件,然后使用Spring 来粘合各个组件,这样就可以很方便地对该程序进行扩展。
今天客户提出一个要求:需要控制这个程序在同一主机上只能启动一个实例。
为了实现客户要求,我首先想到就是在数据库中建一张表,程序启动时往该表中写入一个标志,等程序结束时再删除标志。但这种方
式存在一个问题就是,如果程序是非正常停止或被杀进程,那么这个标志就不可能被清除,那下一次启动就会误判为重复启动;另外,如
果用数据库来记录启动标志的话,还把该程序跟数据库紧密 耦合起来,感觉很别扭。
排除了第一种方案之后,我以想到了用文件来保存启动标志(好象一些大型的程序,诸如weblogic好象就是采用在文件中记录启动标
志方 式来控制重复启动的)。客流量然这种方式不需要与数据库耦合在一起,但也存在程序异常中止而无法清除启动标志的问题,所以这
个方案也被枪毙了。
我想到的第三种方案就是在JAVA中调用操作系统的查看系统进程的方式来取得系统进程,然后再检测系统进程有特殊的进程标志来判
断是否重 复启动。但这种方式一是看起来很别扭,再者就是Window和 *nix系统中查看系统进程的命令不一样,分成几种情况来处理,无
端地增加了程序的复杂性,也不可取。
能不能在内存中记录一个启动标志呢?理论上这应该是不可行的,因为跨JVM来相互操作内存数据是不可能。我在网上搜了一下,也没
找到相关的例子。
那能不能占用一点系统共享资源,来换取我们的目标呢?比较容易想到的系统资源并且不能重复使用的资源就是端口。我尝试采用如
下方案:在程序中指定一个不常用的端口(比如:12345),在程序启动时,就指定的端口启动一个ServerSocket,这个Socket只是为了占
用这个端口,不接受任何网络连接。如果试图启动第二个实例时,程序在该指定端口启动ServerSocket时就会抛异常,这时我们就可以认
为系统已经启动过了,然后打印提示并直接退出程序即可。这种方式在理论上分析应该可以的,我开始动手修改程序。程序修改如下:
java 代码package cn.com.pansky.xmdswz.application.scheduler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.SchedulerException;
import org.quartz.impl.StdScheduler;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.com.pansky.xmdswz.system.cache.CachedTableMgr;
import cn.com.pansky.xmdswz.system.config.SystemConfig;
import cn.com.pansky.xmdswz.utility.DateUtil;
import org.quartz.JobDetail;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import java.net.ServerSocket;
import java.io.*;
/**
* Title: XXXXXXX
* Description: XXXXXXXXXXXX
* Copyright: Copyright (c) 2006
* Company: www.pansky.com.cn
*
* @author Sheng Youfu
* @version 1.0
*/
public class Scheduler {
private static Log log = LogFactory.getLog(Scheduler.class);
private static ServerSocket srvSocket = null; //服务线程,用以控制服务器只启动一个实例
private static final int srvPort = 12345; //控制启动唯一实例的端口号,这个端口如果保存在配置文件中会更灵活
/**
* 定时任务配置文件
*/
private static String CONFIG_FILE = "cn/com/pansky/xmdswz/application/scheduler/Scheduling-bean.xml";
public Scheduler() {
//检测系统是否只启动一个实例
checkSingleInstance();
//下面读取Spring的配置文件
SystemConfig cfg = new SystemConfig();
String config = cfg.parseParam("SCHEDULER.CONFIG_FILE", false);
if(config!=null && !"".equals( config.trim()))
CONFIG_FILE = config;
log.debug("CONFIG_FILE: "+CONFIG_FILE);
}
/**
* 主函数
* @param args String[]
* @throws Exception
*/
public static void main(String[] args) throws Exception{
Scheduler sch = new Scheduler();
sch.execute();
}
/**
* 运行定时任务
*/
public void execute() {
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {CONFIG_FILE});
BeanFactory factory = (BeanFactory) appContext;
/**
* 装载任务调度
*/
StdScheduler scheduler = (StdScheduler) factory.getBean("schedulerFactoryBean");
//先暂停所有任务,等待装载缓存代码表
try {
scheduler.pauseAll();
} catch (SchedulerException ex) {
log.error("",ex);
}
/**
* 装载缓存代码表
*/
CachedTableMgr cachedtableMgr = (CachedTableMgr) factory.getBean("cachedTableMgr");
try {
cachedtableMgr.loadCodeTable();
} catch (Exception ex) {
log.fatal("Load cached table failed. System will exit.", ex);
System.exit(0);
}
//重新恢复所有任务
try {
scheduler.resumeAll();
} catch (SchedulerException ex) {
log.error("",ex);
}
}
/**
* 检测系统是否只启动了一个实例
*/
protected void checkSingleInstance() {
try {
srvSocket = new ServerSocket(srvPort); //启动一个ServerSocket,用以控制只启动一个实例
} catch (IOException ex) {
if(ex.getMessage().indexOf("Address already in use: JVM_Bind")>=0)
System.out.println("在一台主机上同时只能启动一个进程(Only one instance allowed)。");
log.fatal("", ex);
System.exit(0);
}
}
} 经过测试,程序能很好地满足我们的要求,问题解决。
我之所以称这种方式另类,是因为这种方式以牺牲一个端口的代价来达到我们的设计要求,采用这种方式的人应该不多。但我认为,
只要我们牺牲的代价与我们的目标比较起来是在可接受的范围内,这种方式就是可取的,这与我们花钱增加内存来让程序运行更快在本质
应该是相同的。
2,配置文件config记录也是可以的,改写文件的时候lock锁定一下.
比如:
javaw -jar SomeJar.jar args
使用已经启动的实例来处理args