关于定时器的总结

        项目中用到过定时器的知识点,发现其实并不简单,在此总结下。关于定时目前2种技术能解决,一种是轻量级的Java Timer,另一种是重量级的Quartz。


关于Java Timer

          Timer是jdk中提供的一个定时器工具,使用的时候会在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次。TimerTask是一个实现了Runnable接口的抽象类,代表一个可以被Timer执行的任务。


关于Timer一个简单的例子:

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            public void run() {
                System.out.println("11232");
            }
        }, 200000 , 1000);

       schedule是一个定时调度的方法,里面有多个方法,每个方法代表的意义不同,例如当前的Demo表示的是200秒后开始执行,每一秒执行一次。Timer提供的定时调度的方法很多,能满足多种场景:


1、这个方法是调度一个task,经过delay(ms)后开始进行调度,仅仅调度一次。

 public void schedule(TimerTask task, long delay)

2、在指定的时间点time上调度一次。

public void schedule(TimerTask task, Date time)

3、这个方法是调度一个task,在delay(ms)后开始调度,每次调度完后,最少等待period(ms)后才开始调度。

public void schedule(TimerTask task, long delay, long period)

4、和上一个方法类似,唯一的区别就是传入的第二个参数为第一次调度的时间。

public void schedule(TimerTask task, Date firstTime, long period)


5、调度一个task,在delay(ms)后开始调度,然后每经过period(ms)再次调度,貌似和方法:schedule是一样的,其实不然,后面你会根据源码看到,schedule在计算下一次执行的时间的时候,是通过当前时间(在任务执行前得到) + 时间片,而scheduleAtFixedRate方法是通过当前需要执行的时间(也就是计算出现在应该执行的时间)+ 时间片,前者是运行的实际时间,而后者是理论时间点,例如:schedule时间片是5s,那么理论上会在5、10、15、20这些时间片被调度,但是如果由于某些CPU征用导致未被调度,假如等到第8s才被第一次调度,那么schedule方法计算出来的下一次时间应该是第13s而不是第10s,这样有可能下次就越到20s后而被少调度一次或多次,而scheduleAtFixedRate方法就是每次理论计算出下一次需要调度的时间用以排序,若第8s被调度,那么计算出应该是第10s,所以它距离当前时间是2s,那么再调度队列排序中,会被优先调度,那么就尽量减少漏掉调度的情况。

public void scheduleAtFixedRate(TimerTask task, long delay, long period)

6、方法同上,唯一的区别就是第一次调度时间设置为一个Date时间,而不是当前时间的一个时间片,我们在源码中会详细说明这些内容。

public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period)


关于Timer在实际使用方面,和java多线程一样有两种方式:

一种是通过多线程的方式运行:


package cn.xdf.dtmanager.quartz;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;

/**
 * Created by fengch on 2017/9/21.
 */
public class MyTask extends TimerTask {
    private String name;
    private int age;
    public MyTask(String name, int age) {
        this.name = name;
        this.age = age;
    }


    @Override
    public void run() {
        SimpleDateFormat sdf = null;
        sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        System.out.println("当前时间:" + sdf.format(new Date()) + "---------------------" + this.name + ":::" + this.age);
    }
}

Timer t = new Timer(); // 建立Timer对象
t.schedule( new MyTask("多吃点", 1), DateUtil.StringToDate(time1,DateUtil.DATATIME_FORMMATER));



另一种是通过匿名类不类的方式(java8可以通过lambda方式):

       就是如上所写的一个demo!

关于Quartz

       quartz是一个很好的定时job框架,在时间节点配置方面特别的好。所以关于Quartz方面的内容也是特别的多,笔者在这里也是班门弄斧。在多机器部署的时候,可想而知要是一个多个定时都被执行一次,会产生很多错误的可能。所以本篇只介绍使用Quartz这是为leader模式!



多台服务器部署时候,只有其中一台服务器是leader,只有当leader主机挂了的时候,另外一台following并自动成为主机。quartz分三个线程进行完成leader模式的设计:

一、集群任务调度线程

此任务调度的作用是:修改当前服务器的时间为系统的当前时间,将过期的服务状态变更为停止:

package com.lgy.core.task;


import com.lgy.comm.Constant;
import com.lgy.core.base.AbstractTask;
import com.lgy.service.ServInfoService;
import org.apache.log4j.Logger;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


/**
 * 集群任务调度线程的定时任务类
 * @author yuejing
 * @date 2015年3月29日 下午10:05:34
 * @version V1.0.0
 */
@Component
public class MainTask extends AbstractTask {
	private static final Logger LOGGER = Logger.getLogger(MainTask.class);

	@Autowired
	private ServInfoService servInfoService;

	@Override
	public void execute(JobExecutionContext context) {
		System.out.println("=========================== 执行了MainTask ====================");
		//=========================== 发送任务心跳(间隔10s) begin ====================
		//修改当前服务的updatetime为当前时间
		servInfoService.updateUpdatetimeByServid(Constant.serviceCode());
		//将过期的服务状态变更为停止
		servInfoService.updateDestroy();
		//=========================== 发送任务心跳(间隔10s) end ====================
	}
}



二、leader选举调度线程


流程图如下:




三、清除过期调度线程

此任务调度的作用是:删除过期已经销毁的服务,将已经销毁的服务设置为非leader:

package com.lgy.core.task;
import com.lgy.core.base.AbstractTask;
import com.lgy.service.ServInfoService;
import com.lgy.utils.FrameTimeUtil;
import org.apache.log4j.Logger;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 清空任务日志的定时任务类
 * @author yuejing
 * @date 2015年3月29日 下午10:05:34
 * @version V1.0.0
 */
@Component
public class CleanTask extends AbstractTask {
	private static final Logger LOGGER = Logger.getLogger(CleanTask.class);

	@Autowired
	private ServInfoService servInfoService;

	@Override
	public void execute(JobExecutionContext context) {
		System.out.println("=========================== CleanTask ====================");
		String siValue = "7";
		Date siDate = FrameTimeUtil.addDays(FrameTimeUtil.getTime(), - Integer.valueOf(siValue));
		//清空小于指定日期的已停止的服务
		servInfoService.deleteDestroyLtDate(siDate);
		
		//修改已销毁的服务为非Leader
		servInfoService.destroyLeader();

	}
}


有兴趣参考:

https://gitee.com/yuejing/task 

猜你喜欢

转载自blog.csdn.net/fengchao2016/article/details/78082303