一个JAVA后台程序的设计方案 选择自 glchengang 的 Blog
关键字 java.util.Timer 定时器 后台
很多系统都需要一个在后台不间断运行的程序,以定期执行某些系统任务。这类似于Windows中自带的计划任务的功能。我一年半前我参与某省联通的网管项目的开发,曾经写过一个这样的后台程序,它要不间隔的从各种类型服务器上,下载各种类型的数据文件(每个文件都有几兆大小),并将这些文件解读成一条条记录插入到数据库中。这个后台程序直接使用java中的线程,由于线程的复杂性,调试也困难,很不幸这个后台程序很不稳定,每周都会有一两次会停在那里不再往下执行,原因至今天未找到,成为我心中永远的痛。
时隔今日,再次有幸参与IBM一个开发项目,这个项目同样需要一个类似的后台运行程序,这个程序的任务是:每隔一天检查数据库中的数据,并对符合某些条件记录进行某操作。任务很简单,为了今后扩展方便,我将这个设计成了一个多任务可管理的后台程序。周未我设置了两个任务同时执行,一任务每10秒执行一次,另一任务每1秒执行一行,运行了两天,运行较果良好。估计很多朋友会面临与我同样的问题,在此将程序思路和代码公布,希望有兴趣的朋友大家一起研究探讨。
一、程序运行界面:
1、总控制台
2、计划任务的设置界面。
3、控制台输出结果。
二、程序开发环境:
使用Java(JDK 1.4)开发,图形界面使用Eclipse (2.1.3版)的SWT方式开发。运行主机:P4 2.6+1G内存 windowsXP操作系统
三、预备。
开发此类程序,最好不要直接使用JAVA的线程来编程,这样会增加不必要的复杂度和难度,吃力不讨好。在JAVA中有一个包 java.util.Timer 这个包封装了对线程的操作,我们可以把它称做定时器类。我们先来看一个简单例子:
import java.util.Timer;
import java.util.TimerTask;
public class Reminder {
Timer timer;
public Reminder(int seconds) {
timer = new Timer();
timer.schedule(new RemindTask(),seconds*1000); //参数要求转化成毫秒
}
public static void main(String args[]) {
new Reminder(5); //5秒后运行
}
/**一个内部类,封装了所要运行的任务*/
class RemindTask extends TimerTask {
public void run() {
System.out.println("任务运行。。。。");
timer.cancel(); //结束timer中的所有任务
}
}
}
这里涉及于两个JAVA类Timer和TimerTask。我们继承TimerTask类后,将所要运行的任务封装其run方法中;Timer可以管理几千个任务(TimerTask),注意,同一个任务对象不能两次加入到Timer中执行。
对(虽然执行的任务都一样,但是两个任务对象):
timer.schedule(new RemindTask(), seconds * 1000);
timer.schedule(new RemindTask(), seconds * 1000);
错
RemindTask task= new RemindTask();
timer.schedule(task, seconds * 1000);
timer.schedule(task, seconds * 2000);
四、设计方案。
主要的类图
说明:
任务类的设计。我们先创建一个抽象类AbstractTimerTask,这个类直接继承至TimerTask类,提供对TimerTask封装。然后所有具体的任务类(如:TimerTask_1)继承自AbstractTimerTask。
import java.util.TimerTask;
public abstract class AbstractTimerTask extends TimerTask {
TaskEntry taskEntry; //任务记录
public AbstractTimerTask(TaskEntry taskEntry) {
this.taskEntry = taskEntry;
}
/*
* 生成一个新的实例相当于克隆自身;原因在于:同一任务对象不能两次加入到Timer
* 在TaskEntry类将看到它的使用方法
*/
abstract AbstractTimerTask getCloneObject();
}
下面是它的一个实现类的源代码我们可以将要运行任务的代码写在这个类中。
import java.util.Calendar;
public class TimerTask_1 extends AbstractTimerTask {
public TimerTask_1(TaskEntry taskEntry) { //构造方法
super(taskEntry);
}
public AbstractTimerTask getCloneObject() {
return new TimerTask_1(taskEntry);
}
public void run() {
/*在这里写你要执行的程序。。。。。*/
System.out.println("??时:"+taskEntry.getName()+"运行了一次");
this.taskEntry.taskStart(); //运行下一个时间点任务
}
}
在AbstractTimerTask类有一个TaskEntry字段,这是本设计的一个核心类,它代表一条封装完整的任务记录,每个任务类和它的运行计划都封装在这条类中,源代码如下。Timer和AbstractTimerTask前面都已说过,那么TimePlan是做什么用的呢?
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import mytimer.util.Util;
/**任务记录类*/
public class TaskEntry {
public static final int TASK_START = 0; //定义两个表示任务记录状态常量
public static final int TASK_STOP = 1;
private Long oid; //任务ID号,唯一
private String name; //任务名称
private int state = TASK_STOP; //任务状态(启动/停止)
private Timer timer; //JAVA计时器
private TimePlan timePlan; //时间计划的类型
private AbstractTimerTask timerTask; //任务类的种子对象,由这个对象来不断克隆
private AbstractTimerTask runTimerTask; //运行计划的当前任务
/**
* ITaskEntry.taskStart()-->TimerTask.run()-->ITaskEntry.taskStart()
* 形成一个循环回路。本方法负责起动本类代表的任务
*/
public void taskStart() {
if (timePlan.haveNext()) {
Date date = timePlan.nextDate();//得到任务计划时间
runTimerTask = timerTask.getCloneObject();//得到任务(复制)
timer.schedule(runTimerTask, date); //加入计划队列
//打印将要运行的计划任务的信息
Calendar c = Calendar.getInstance();
c.setTimeInMillis(runTimerTask.scheduledExecutionTime());
System.out.println(Util.dateToLongStr(c.getTime())+"将运行"+name);
} else {
state = TASK_STOP;
System.out.println(name + "结束");
}
}
/**停止任务*/
public void taskStop() {
if (runTimerTask != null) {
//打印信息
Calendar c = Calendar.getInstance();
c.setTimeInMillis(runTimerTask.scheduledExecutionTime());
System.out.println("计划于:"+Util.dateToLongStr(c.getTime())+"运行的" + name + "被终止");
//终止本任务, 调用Timer.cancel()是终止Timer的所有任务。
runTimerTask.cancel();
} else {
System.out.println(name + "未进入执行计划");
}
}
……… 一些属性的get/set方法(省略)
/** 监听类(内部类) */
public static class DateBeforeTodayException extends NullPointerException {
private Date date;
public DateBeforeTodayException(Date date) {this.date = date;}
public String toString() {
return "计划时间(" + Util.dateToLongStr(date) + ")早于当前时间";
}
}
}
1、TimePlan是一个接口(interface),它是表示“运行计划的方案”,这个程序中提供了三种运行计划方案(见前图:计划任务的设置界面):
一次性运行。
每隔一个时间段运行。
一周中选择那几天运行。
将它设计成一个接口是为了方便今后扩展,如果要新增新的时间方案只需要继承这个接口写一个新的实现即可。三种时间方案的类图如下:
说明:
a) TimePlan封装了五个方法,其它haveNext()和nextDate()最重要,这个两个方法模仿了Java中集合类(Collection)的迭代器(Iterator)的设计形式,代码如下:
import java.util.Date;
//时间计划方案的接口
public interface TimePlan {
boolean haveNext();//判断还有没有下一个计划时间
Date nextDate();//得到下一个计划时间
Date getCurrentDate();//得到开始时间
void setCurrentDate(Date date); //设计开始时间
String getTimePlanString();//显示运行计划方案的文字说明
}
b) AbstractTimePlan是这个抽象类,主要目的是将一些各子类的公共方法写在这里。代码如下:
import java.util.Date;
public abstract class AbstractTimePlan implements TimePlan {
//记录计划的第一时间点,除设置新的起始时间,否则不再改变
protected Date currentDate;
/*
当前计划的时间点,每次计划替换时被更新,
似乎这个才应叫cureentDate,sorry不想再改了
*/
protected Date planDate;
public boolean haveNext() {
return (planDate != null);
}
public Date getCurrentDate() {
return currentDate;
}
public void setCurrentDate(Date date) {
currentDate = date;
planDate = date; //在赋给currentDate值时,同时也赋给planDate
}
}
c) 然后我们看看三种计划方案的实现类的源代码:
//“一次性运行”的计划方案类
import java.util.Date;
public class TimePlanOnce extends AbstractTimePlan {
public Date nextDate() {
//把要当前的计划时间保存在中间变量中
Date returnDate = this.planDate;
//算出下一个计划时间。没有下一个就设为null
this.planDate = null;
//判断一下计划时间合不合条件
if (returnDate == null)
throw new NullPointerException("没有下一个计划日期");
return returnDate;
}
public String getTimePlanString() {
return "一次性运行,运行时间: (打印this.currentDate) ";
}
}
//“周期性间隔”的时间计划方案类
import java.util.Date;
public class TimePlanPeriod extends AbstractTimePlan {
public static final int HOUR = 0;
public static final int DAY = 1;
private int spaceTime; //间隔时间,单位毫秒
private int timeType;
public Date nextDate() {
//把要当前的计划时间保存在中间变量中
Date returnDate = this.planDate;
//算出下一个计划时间。没有下一个就设为null
int milliSecond = 0;
if (timeType ==HOUR) milliSecond = spaceTime * 1000; //小时*60*60*1000;
if (timeType ==DAY) milliSecond = spaceTime * 24 * 60 * 60 * 1000; //天
planDate = Util.DateAddSpaceMilliSecond(planDate, milliSecond);
//判断一下计划时间合不合条件
if (returnDate == null)
throw new NullPointerException("没有下一个计划日期");
return returnDate;
}
public String getTimePlanString() {
if (timeType == HOUR)
return "第一次运行于:currentDate.并每隔spaceTime小时运行一次";
if (timeType == DAY)
return "第一次运行于:currentDate.并每隔spaceTime天运行一次";
return "";
}
public int getSpaceTime() { return spaceTime; }
public int getTimeType() { return timeType; }
public void setSpaceTime(int i) { spaceTime = i; }
public void setTimeType(int i) { timeType = i; }
}
/**选择一周的某几天,让这几天在同一时间点运行任务, 一周内必须选择一天*/
import java.util.Calendar;
import java.util.Date;
public class TimePlanSelectWeek extends AbstractTimePlan {
private static Calendar c = Calendar.getInstance(); //取得一个日历实例
private static int spaceMilliSecond = 0; //间隔时间,单位毫秒
private boolean[] selectWeek = new boolean[7]; //0为星期日 ,1为星期一
public Date nextDate() {
Date returnDate = null;
if (!isSelectWeek(planDate)) //如果这一天不是所选周中的一天
planDate = getNextDate(planDate);
returnDate = planDate;
planDate = getNextDate(planDate);
//判断一下计划时间合不合条件
if (returnDate == null)
throw new NullPointerException("没有下一个计划日期");
return returnDate;
}
//算出下一个计划时间。没有下一个就设为null
private Date getNextDate(Date date) {
Date tempDate = date;
Date returnDate = null;
for (int i = 0; i < 7; i++) {
tempDate = Util.DateAddSpaceMilliSecond(tempDate, spaceMilliSecond);
if (isSelectWeek(tempDate)) {
returnDate = tempDate;
break;
}
}
return returnDate;
}
/**设置某星期是否被选, 0为星期日 ,1为星期一....6为星期六*/
public void setSelectWeek(int i, boolean b) {selectWeek[i] = b;}
/** 判断某星期是否被选*/
public boolean isSelectWeek(int i) {return selectWeek[i];}
/**判断某天所属星期几是否被选*/
public boolean isSelectWeek(Date date) {
if (date == null) return false;
c.setTime(date);
//Calendar.DAY_OF_WEEK:星期日=1,星期六=7 c.get(Calendar.DAY_OF_WEEK)
return isSelectWeek(c.get(Calendar.DAY_OF_WEEK) - 1);
}
public String getTimePlanString() {
StringBuffer sb = new StringBuffer("");
if (selectWeek[1]) sb.append("周一,");
if (selectWeek[2]) sb.append("周二,");
if (selectWeek[3]) sb.append("周三,");
if (selectWeek[4]) sb.append("周四,");
if (selectWeek[5]) sb.append("周五,");
if (selectWeek[6]) sb.append("周六,");
if (selectWeek[0]) sb.append("周日,");
return "每周的"+sb.toString()+"运行";
}
}
TimerTask的工厂类。将生成TimerTask的代码另起一个类的好处是代码的层次比较清楚,也比较好管理。由于TimerTask包含有几个字段,因此产生一个TimerTask对象还是有一定的复杂度,建立一个专门生成TimerTask的工厂类,这样我们在生成一个TimerTask对象时就可以少掉很多麻烦的代码了。当然由于我的工作任务,只需要一个TimerTask对象就够了,所以最初之前我是将它直接写在图形界面的代码里的。
这里建立一个TimerTask对象池tasks,它是一个静态变量,这样在getInstance时不必总是要新生成一个TimerTask。还有Timer也是一个静态变量,它是一个全局单例(是最简单的单例模式了),因为前面说了Timer可以管理几千个任务,所以Timer对象一个就够了。
import java.util.HashMap;
import java.util.Timer;
public class TaskEntryFactory {
private static final HashMap tasks = new HashMap();
private static final Timer timer = new Timer();
public static TaskEntry getInstance(Long oid, String name) {
if (tasks.containsKey(oid)) {
return (TaskEntry) tasks.get(oid);
} else {
TaskEntry entry = new TaskEntry();
entry.setOid(oid);
entry.setName(name);
entry.setTimer(timer);
entry.setTimerTask(new TimerTask_1(entry));
tasks.put(oid, entry);
return entry;
}
}
}
起动和停止任务,当“任务设置界面(TaskListDialog.java)”点击OK后处理。界面的编写就不在本文讨论的范围内了。
//任务设置界面中点击”确认(OK)”按钮后的处理
if (dialog.open() == Window.OK) {
if (taskEntry.getState() == TaskEntry.TASK_START) {
taskEntry.taskStop();//将旧的停掉
taskEntry.taskStart();//开始新设置的
}
if (taskEntry.getState() == TaskEntry.TASK_STOP)
taskEntry.taskStop();
tv.refresh(taskEntry);
}
分享到:
相关推荐
在Java后台程序设计中,多线程互斥与同步控制是关键的概念,它们确保了并发执行的线程在访问共享资源时的安全性。线程互斥是指在同一时刻,只有一个线程能够访问特定的共享资源,以防止数据的不一致性。线程同步则是...
Java后台开发中常用的缓存解决方案有Redis和Memcached,它们提供高效的键值对存储,适用于临时存储热数据。 9. **消息队列MQ**: 消息队列如RabbitMQ、ActiveMQ等,用于解耦系统间的通信,提高系统的异步处理能力...
在“微信小程序+Java后台完整代码”中,我们可以看到一个完整的电商解决方案,包括前端的小程序部分和后端的Java服务。小程序商城的部分主要负责展示商品、处理用户交互、实现购物车功能、订单管理以及支付流程等。...
在这个项目中,我们结合了微信小程序和Java后台技术,实现了一个完整的微信支付流程。以下将详细阐述涉及的知识点: 1. **微信小程序**:微信小程序是一种轻量级的应用开发框架,允许开发者在微信内部创建和运行...
综上所述,"微信商城小程序带JAVA后台"项目涵盖了从前端用户体验到后台业务处理的完整流程,涉及微信小程序开发、JAVA编程、数据库管理、服务器部署等多个技术领域,是企业级电商解决方案的一个实例。
本文将深入探讨一个结合了小程序前端与Java后台的商城项目,旨在为开发者提供一个完整的线上购物平台解决方案。 一、小程序前端 1. **微信小程序**:作为移动端的轻量级应用,微信小程序以其无需下载、即开即用的...
总的来说,【JAVA后台管理系统】是一个涵盖多领域功能的集成平台,利用先进的技术和工具,为企业提供了全面的信息化解决方案。无论是日常办公、客户管理,还是内容发布,都能够在这个平台上得到高效且便捷的管理。...
Java后台管理框架是开发企业级应用的重要组成部分,它们通常包含了权限管理、数据处理、界面设计等多个方面的功能,帮助开发者快速构建稳定、高效、安全的后台系统。以下将介绍6个国内优秀的Java后台管理框架,这些...
综上所述,Java后台数据隔离的实现涉及角色权限设计、AOP技术的应用、数据权限管理机制的建立以及XML配置文件中的SQL动态生成。这种方案可以灵活适应不同的业务需求,为用户提供安全、可控的数据访问环境。
点餐系统是一个集成微信小程序和Java后台的在线订餐解决方案,尤其适合于在校学生进行课程设计或毕业设计项目。此系统充分利用了现代Web技术,旨在为用户提供便捷的订餐体验,同时也为餐厅提供了高效的订单管理平台...
Java后台面试题通常涵盖广泛的领域,包括但不限于编程基础、面向对象设计原则、Servlet技术以及数据库管理。以下是对这些知识点的详细解析: 1. **面向对象(Object-Oriented Programming,OOP)**: - **封装**:...
【标题】:“java通用后台管理系统”是一个基于Java技术栈的后台管理系统,主要利用SpringBoot框架进行构建,旨在提供一套高效、便捷的管理平台。 【描述】:该项目采用Maven作为项目构建工具,确保了依赖管理和...
综合来看,基于JAVA开发的微信小程序购物商城项目是一个涵盖了前后端开发、数据库设计、用户认证、支付接口集成等多个方面的综合性项目。它不仅需要开发者具备扎实的编程基础,还需要对电商业务有深入的理解,以实现...
这套"培训机构系统全套源码(微信小程序 后台Java).zip"包含了一个完整的教育培训平台的开发资源,主要用于构建一个微信小程序应用以及对应的Java后台服务。这个系统可能涵盖了用户管理、课程展示、报名注册、在线...
总的来说,这个基于 Java EE 的权限后台管理系统是一个集成了多种技术的综合解决方案,旨在提供安全、高效和易于维护的企业级权限管理服务。通过合理的设计和框架选择,它可以适应各种应用场景,并确保数据安全和...
【标题】:“JAVA后台管理系统,企业开发首选脚手架2.后台采用Springboot框架+ Shiro权限+ Mybatis.zip” 这个标题揭示了该压缩包文件包含一个基于Java的后台管理系统,它被设计为企业级开发的首选工具。系统的核心...
这个项目提供了一个完整的抽奖系统解决方案,包括接口设计、前端展示和后台管理,对于学习Java Web开发或者理解前后端协作流程具有很高的参考价值。开发者可以在此基础上根据实际需求进行定制和扩展,例如增加新的...
总的来说,AmaAdmin作为一个基于Bootstrap的Java后台管理系统,它集成了前端的美观性和后端的业务处理能力,为企业提供了一个高效、易用的管理工具。无论是对于开发人员还是终端用户,都能从中受益,实现高效、稳定...
总的来说,这个Java后台模板是一个完整的解决方案,可以帮助开发者快速搭建后台服务,减少重复劳动,提高开发效率。通过合理利用模板,开发者可以更专注于业务逻辑的实现,从而提升项目的整体进度和质量。在使用过程...
3. **MVC(Model-View-Controller)架构**:许多后台模板遵循MVC设计模式,它将应用逻辑分为模型、视图和控制器三个部分,有助于代码组织和解耦。学生需要理解各部分的职责,并学会在实际项目中应用。 4. **框架的...