定时器
开发环境:JDK1.7
需求
1)每隔一段时间来完成某一特定的事
2)参数:(未定类型 时间,String 方法名,Map 参数)
思考:没有执行类,那么如何执行操作呢,只传个方法名和参数有卵用
需求改动
2)–>2.1)增加字节码参数 Class 操作类
再次思考:用Map来装载参数合适吗?我可以通过反射来获取形参的名字吗?
百度中…
1.http://www.cnblogs.com/guangshan/p/4660564.html
该方法工作量太大了,所以拒绝使用,但有空来学习一下也不错
继续百度中…
2.http://blog.youkuaiyun.com/revitalizing/article/details/71036970
天啊!是JDK8的…无奈…
继续百度中…
3.好吧,事实证明只通过JDK1.7的API是获取不了参数名的,所以不使用Map来存储参数了,改为数组来存储参数吧..那么要求传过来的参数的顺序与方法定义的顺序相同就行了…
此时的参数为:
(未定类型 时间,Class 操作类, String 方法名,Object[] 参数)
定时器怎么定义
查看一下现存代码的定时器是怎么搞的
//0.定义一个执行操作的任务器
class MyTask extends TimerTask {
@Override
public void run() {//任务操作}
}
//1.先搞一个定时器
Timer timer = new Timer();
//2.调用,开始计时调用吧
//第一个参数,执行操作的类TimerTask
//第二个参数,延迟时间,单位毫秒
//第三个参数,执行时间间隔,单位毫秒
//该方法有有多个重载方法,详情参考API
timer.schedule(new MyTask(), 0, 1000);
问题又来了,参数怎么传进任务器里?
1)在TimerTask定义一个public方法,然后再run方法里调用public方法
2)通过回调机制来实现参数的传递,Thank GOD!听上去非常高大上!
实现细节是这样的:自己百度去:)
不用该方法,有简单的不用,干嘛呢
如果使用回调机制我还需要写个接口,通过接口里方法所返回的值来获取数据,这么的方式明显不是一个好方法,所以就不用了
问题又来了,该在哪里启动定时器的schedule方法呢 ?
1)单独建立一个类来作为工具类,里面的public方法可以让使用者来调用来启动任务,当然,需要传参进来,我再把参数传到TimerTask
2)在TimerTask的public方法启动,把所存参数存到属性里,在run方法里实现操作,而不再run里调用public方法里,或者再写一个private方法,在private方法里实现操作,再在run方法里调用private方法.
由于我的定时器的操作是单一的,所以就不用再创建一个private方法来封装操作代码了.
暂时定时器工具类的框架就这么定了
demo
public class MyTimerUtil extends TimerTask {
@Override
public void run() {
System.out.println("hello timer!");
}
public <T> Object invokeTimer(long millisecond, Class<T> clazz, String mehtodName, Object... params) {
Timer timer = new Timer();
timer.schedule(this, 0, millisecond);
return null;
}
public static void main(String[] args) {
MyTimerUtil myTimerUtil = new MyTimerUtil();
Object myResult = myTimerUtil.invokeTimer(1000L, null, null);
System.out.println(myResult);
}
}
总体框架demo
public class MyTimerUtil extends TimerTask {
private Method method;
private Object[] params;
private Object instance;
@Override
public void run() {
try {
method.invoke(instance, params);
System.out.println("hello method");
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> void invokeTimer(long millisecond, Class<T> clazz, String mehtodName, Object... params) throws Exception {
method = clazz.getMethod(mehtodName);
this.params = params;
instance = clazz.newInstance();
Timer timer = new Timer();
timer.schedule(this, 0, millisecond);
}
public static void main(String[] args) throws Exception {
MyTimerUtil myTimerUtil = new MyTimerUtil();
myTimerUtil.invokeTimer(1000L, MyTimerUtil.class, "testMethod");
}
public void testMethod() {
System.out.println("in testMethod");
}
}
还有什么更好地办法实现定时器呢?
百度中…
http://blog.youkuaiyun.com/haorengoodman/article/details/23281343/
使用ScheduledExecutorService方法,接下来会在下面写个demo
使用这个的优势是:(这是从上面的连接copy下来的)
ScheduledExecutorService是从Java SE5的java.util.concurrent里,做为并发工具类被引进的,这是最理想的定时任务实现方式。
相比于上两个方法,它有以下好处:
1>相比于Timer的单线程,它是通过线程池的方式来执行任务的
- 2>可以很灵活的去设定第一次执行任务delay时间
- 3>提供了良好的约定,以便设定执行的时间间隔
demo
public class MyScheduledExecutorUtil implements Runnable{
private Method method;
private Object[] params;
private Object instance;
public <T> void invokeTimer(int second, Class<T> clazz, String mehtodName, Object... params) throws Exception {
method = clazz.getMethod(mehtodName);
this.params = params;
instance = clazz.newInstance();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);//线程池的大小,允许的最大线程数
scheduledExecutorService.scheduleAtFixedRate(this, 0, second, TimeUnit.SECONDS);
}
@Override
public void run() {
try {
method.invoke(instance, params);
System.out.println("hello method");
} catch (Exception e) {
e.printStackTrace();
}
}
public void testMethod() {
System.out.println("in testMethod");
}
public static void main(String[] args) throws Exception {
MyScheduledExecutorUtil myTimerUtil = new MyScheduledExecutorUtil();
myTimerUtil.invokeTimer(1, MyTimerUtil.class, "testMethod");
}
}
再次提醒
本项目使用目录的"总体框架demo"作为工具类模板开发,以下简称该工具类模板为"工具类"
怎么停止定时器?
Timer有个cancel方法,可以停止定时器
TimerTask也有个cancel方法,可以取消该TimerTask任务,
我们不是timer.schedule(TimerTask, delay, period);定时执行TimerTask任务的吗?
他们的区别是Timer的cancel是把整个定时器都退出了,而TimerTask的是把任务停止了,但定时器还在工作,尽管定时器里已经没有任务了,定时器还是在监听着,耗费着资源
我们的需求就是要停止定时器,所以调用Timer的cancel.
把Timer定义为静态属性
原因:
1)Timer为静态属性,可以被所有"工具类实例"共享
2)每一个实例实质是一个TimerTask,每次创建工具类实例并执行invokeTimer方法,相当于使用同一个Timer开启一个线程来运行TimerTask任务,每运行一个TimerTask去创建一个Timer实例就太浪费资源了
3)若想停止各自的TimerTask,则可以调用工具类的cancel方法,而不影响其他任务
4)若想关闭Timer,则可以使用Timer的cancel方法,当然,该方法封装在工具类且声明为public以供调用
*5)说了这么多废话,现在才是真正目的:方便关闭Timer
若Timer不声明为静态,那么我想关闭Timer的时候,我先要找出工具类的实列,再通过工具类的实例的已封装的关闭方法去关闭.
问题来了,我作为一个工具类去供人使用,我哪知道他人在哪创建了实例,若我想在非创建实例的类里去关闭Timer,它都不是在我这个类里创建的,那我如何获取该实例呢去关闭Timer呢?
若Timer声明为静态属性那么就好办了,直接"类名.关闭方法",哈哈哈,就关闭了,多么的方便..
若真的只想关闭使用者自己的任务而不影响其他人的任务,那就简单了,"实例.cancel()"就可以来,我都说了,我们的工具类实质是一个TimerTask,其本来就有一个取消任务所用的方法cancel,至于如何在非创建实例的类获取实例的问题,我可就不管了,最起码解决了关闭Timer的问题
问题又来了,反射获取方法需要参数类型
解决有两种方法:
- 通过调用者传进的参数获取其Class字节码,但是如果其传进的为基本类型那么就崩了,因为通过Object[]来获取参数时,会自动打包,如int会打包为Integer,此时类型不匹配,找不到方法,(PS:我测试过许多遍了)所以就不用该方法了
- 就让调用者传递那参数类型的字节码过来,想用就要按照规定的来.
发现东西了,可变参数
可变参数若只传null,则会为null
public static void main(String[] args) throws Exception {
test(null);
}
private static void test(Object... a) {
System.out.println(a);
}
输出:null
传多个null.则会不为null
public static void main(String[] args) throws Exception {
test(null,null);
}
private static void test(Object... a) {
System.out.println(a+" length="+a.length+" a[1]="+a[1]);
}
输出:[Ljava.lang.Object;@45486b51 length=2 a[1]=null
不传时,不为null,其数组大小为0
public static void main(String[] args) throws Exception {
test();
}
private static void test(Object... a) {
System.out.println(a+" length="+a.length);
}
输出:[Ljava.lang.Object;@2a97cec length=0
又发现问题了,new Timer()后就会开始监听
那么静态变量何时加载:百度有个说法:
在第一次创建一个类的对象或者第一次调用一个类的静态属性和方法的时候,会发生类加载
类加载期间,如果发现有静态属性,就给对应的静态属性分配内存空间,并赋值
问题在现,Timer的cancel()问题
Timer执行cancel()方法后Timer不会置为null,但是该对象已经不能再用了
那么如何判断Timer是否被cancel了?
1)cancel后将Timer置为空
2)Timer cancel后再去cancel会抛异常,可以通过捕获异常来判断是否已经被cancel了
解决cancel问题和静态Timer加载后监听问题
1)在封装的cancel方法将Timer置为空
public void cancelTimer() {
if(timer!=null) {
timer.cancel();
timer = null;
}
}
2)在构造方法里new Timer(),避免任何第一次加载工具类时都触发new Timer 从而监听,浪费资源
public TimerUtils() {
if(timer == null) {
timer = new Timer();
}
}
最终的结果如下
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
/**
* 1.定时器工具类,定时器Timer为static,所有实列共享<br>
* 2.该工具类提供了cancelTimer()方法,以终止定时器的运行<br>
* 3.该工具类本身继承了TimerTask类,故若想终止某一实例所开启的任务,可以直接调用继承方法cancel(),只终止任务,不终止定时器
*/
public class TimerUtils extends TimerTask {
private Method method;
private Object[] params;
private Object instance;
private static Timer timer;//全部实例共享的定时器
/**
* Timer在构造方法里实例化
*/
public TimerUtils() {
if(timer == null) {
timer = new Timer();
}
}
@Override
public void run() {
try {
method.invoke(instance, params);
} catch (Exception e) {
e.printStackTrace();
this.cancel();//调用方法失败,取消该任务,但不终止Timer,Timer可能存在有其他任务
}
}
/**
* 反射获取方法
* @param clazz 被调用方法的类的字节码,用来获取Method实例
* @param mehtodName 方法名
* @param paramTypes 参数类型,注意:基本类型与其包装类是有区别的,如:int与Integer
* @param params 参数
* @throws NoSuchMethodException
* @throws InstantiationException
* @throws IllegalAccessException
*/
private void initMehtod(Class<?> clazz, String mehtodName, Class<?>[] paramTypes,
Object... params) throws Exception {
if(!isAccessableMehtod(clazz, mehtodName, paramTypes, params)) {
throw new Exception("所传参数不符合反射获取方法或者调用方法的最低条件");
}
try {
method = clazz.getMethod(mehtodName, paramTypes);
} catch (NoSuchMethodException e) {
method = clazz.getDeclaredMethod(mehtodName, paramTypes);
method.setAccessible(true); //获取方法的访问权限
}
this.params = params;
instance = clazz.newInstance();
}
/**
* 判断参数是否满足反射获取方法或者调用方法的最低条件
* @param clazz 被调用方法的类的字节码,用来获取Method实例
* @param mehtodName 方法名
* @param paramTypes 参数类型,注意:基本类型与其包装类是有区别的,如:int与Integer
* @param params 参数
* @return
*/
private boolean isAccessableMehtod(Class<?> clazz, String mehtodName, Class<?>[] paramTypes, Object... params) {
paramTypes = paramTypes==null || paramTypes.length==0 ? null : paramTypes;
params = params==null || params.length==0 ? null : params;
if(clazz==null || StringUtil.isEmpty(mehtodName))
return false;
if(paramTypes==params || (paramTypes!=null && params!=null && (paramTypes.length == params.length)))
return true;
return false;
}
/**
* @param millisDelay 首次执行的延迟时间,单位:毫秒
* @param millisPeriod 周期,上一次与下一次执行的时间间隔,单位:毫秒
* @param clazz 执行操作的类的字节码
* @param mehtodName 执行操作的方法名
* @param paramTypes 参数类型,注意:基本类型与其包装类是有区别的,如:int与Integer
* @param params 参数
* @throws Exception
*/
public void invokeTimer(long millisDelay, long millisPeriod, Class<?> clazz, String mehtodName, Class<?>[] paramTypes, Object... params) throws Exception {
initMehtod(clazz, mehtodName, paramTypes, params);
timer.schedule(this, millisDelay, millisPeriod);
}
/**
* @param firstTime 首次执行的时间,若时间已过则立即执行
* @param millisPeriod 周期,上一次与下一次执行的时间间隔,单位:毫秒
* @param clazz 执行操作的类的字节码
* @param mehtodName 执行操作的方法名
* @param paramTypes 参数类型,注意:基本类型与其包装类是有区别的,如:int与Integer
* @param params 参数
* @throws Exception
*/
public void invokeTimer(Date firstTime, long millisPeriod, Class<?> clazz, String mehtodName, Class<?>[] paramTypes, Object... params) throws Exception {
initMehtod(clazz, mehtodName, paramTypes, params);
timer.schedule(this, firstTime, millisPeriod);
}
/**
* 终止定时器
*/
public static void cancelTimer() {
if(timer!=null) {
timer.cancel();
timer = null;
}
}
}