延时队列实现自定义定时服务

1.延时队列元素定义

public class DelayTask  implements Delayed {

    /**
     * 开始计时时间 不设置则默认为当前系统时间
     */
    private transient Date taskStartTime = new Date();
    /**
     * 过期时间 不设置则默认1分钟
     */
    private  long taskExpiredTime; // = 60 * 1000
    private   Long tId  ;
    /**
     * 初始设置开始计时时间
     * taskStartTime 开始时间 [String] [yyyy-MM-dd HH:mm:ss]
     * taskExpiredTime 过期时间 [long] 单位:s
     * @param taskStartTime
     * @param taskExpiredTime
     */
    public void initTaskTime(String taskStartTime, long taskExpiredTime,Long tId ) {
        this.tId = tId;
        if(!StringUtils.isEmpty(taskStartTime)) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            try {
                this.taskStartTime = sdf.parse(taskStartTime);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        this.taskExpiredTime = taskExpiredTime;
        this.taskExpiredTime += this.taskStartTime.getTime();
    }

    @Override
    public long getDelay(TimeUnit unit) {
        long delay  =  unit.convert(taskExpiredTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
//        MyLog.info(this.gettId() + " get delay:"+ delay);
        return delay;
    }

    @Override
    public int compareTo(Delayed o) {
        int i =  (this.getDelay(TimeUnit.MILLISECONDS) - ((DelayTask) o).getDelay(TimeUnit.MILLISECONDS)) > 0 ? 1:-1;

        MyLog.debug(this.gettId() +"   Leader id =  :" + (i!= 1 ? this.gettId(): ((DelayTask) o).gettId() )   );
        return i ;
     }

    public Date getTaskStartTime() {
        return taskStartTime;
    }

    public void setTaskStartTime(Date taskStartTime) {
        this.taskStartTime = taskStartTime;
    }

    public long getTaskExpiredTime() {
        return taskExpiredTime;
    }

    public void setTaskExpiredTime(long taskExpiredTime) {
        this.taskExpiredTime = taskExpiredTime;
    }

    public Long gettId() {
        return tId;
    }

    public void settId(Long tId) {
        this.tId = tId;
    }
}

  2.实现延时队列

public class DelayQueueHelper {

    private static final Logger logger = LoggerFactory.getLogger(DelayQueueHelper.class);


    private volatile static DelayQueueHelper delayQueueHelper = null;

    private DelayQueue<DelayTask> queue = new DelayQueue<DelayTask>();

    private DelayQueueHelper() {
    }

    public static DelayQueueHelper getInstance() {
        if(delayQueueHelper == null) {
            synchronized(DelayQueueHelper.class) {
                delayQueueHelper = new DelayQueueHelper();
                new Thread(()->{
                    start();
                }).start();
            }
        }
        return delayQueueHelper;
    }

    public synchronized void addTask(DelayTask task) {
        queue.offer(task);
//        queue.put(task);
    }

    public void removeTask(Long tId ) {
        if(tId == null){
            return;
        }
        for(Iterator<DelayTask> iterator = queue.iterator(); iterator.hasNext();) {
            DelayTask queueTask = (DelayTask) iterator.next();
            if(queueTask.gettId().equals(tId)){
                queue.remove(queueTask);
            }
         }
    }

    public DelayQueue<DelayTask> getQueue() {
        return queue;
    }


    public static void start(){
        DelayQueue queue  = delayQueueHelper.getQueue();
        while (true) {
            DelayTask task = null;
            try {
//                logger.info("等待定时任务");
                //queue.take() ; 阻塞 出现bug 未做优化。。。。改为poll() .非阻塞
                task = (DelayTask) queue.poll();
                if(task == null ){
                    sleep(1000);
                    continue;
                }
                 Long tId =  task.gettId();
                 SchedulerTaskHelper.start(tId);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

}

  3.定义任务接口

public interface SchedulerTask extends Runnable {

    String getDescription();

    void setDescription(String description);

    Runnable getStopCall();

    void setStopCall(Runnable stopCall);

    Runnable getBeforeCall();

    void setBeforeCall(Runnable excuteCall);

    Runnable getAfterCall();

    void setAfterCall(Runnable afterCall);

    default Long getId() {
        return TaskIdSequnce.creatId();
    }


    Long getDelayTime();

    void setDelayTime(Long delayTime);


}

  4.抽象任务

public abstract class AbstractSchedulerTask  implements  SchedulerTask{

    /**
     * 任务结束后的回调函数
     */
    private  Runnable stopCall = null;
    private  Runnable beforeCall = null;
    private  Runnable afterCall = null;
    /**
     * 切面延时执行时间
     */
    private Long  delayTime = 0l ;


    private String description;
    public AbstractSchedulerTask(String description ,Long  delayTime){
        this.description= description;
        this.delayTime = delayTime;
    }

    public AbstractSchedulerTask(String description ){
        this.description= description;
    }



    private Long id = TaskIdSequnce.creatId() ;

    @Override
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void setStopCall(Runnable stopCall){
        this.stopCall = stopCall;
    }

    public Runnable getStopCall(){
        return this.stopCall;
    }

    public Runnable getBeforeCall () {
        return beforeCall;
    }

    public void setBeforeCall (Runnable beforeCall) {
        this.beforeCall = beforeCall;
    }

    public Runnable getAfterCall() {
        return afterCall;
    }

    public void setAfterCall(Runnable afterCall) {
        this.afterCall = afterCall;
    }

    public Long getDelayTime() {
        return delayTime;
    }

    public void setDelayTime(Long delayTime) {
        this.delayTime = delayTime;
    }
}

  5.定时任务执行时间配置表

public class SchedulerTaskConf {
    /**
     * 计划id
     */
    private Long id;
    /**
     * 任务列表
     */
    private SchedulerTaskConf[] confs;
    /**
     * 时间周期, 单位ms
     * 0 表示只执行一次
     */
    private Long interval = 0l ;
    /**
     * 开始时间 [String] [yyyy-MM-dd HH:mm:ss]
     */
    private String taskStartTime  = DateUtil.date2String(new Date(),"yyyy-MM-dd HH:mm:ss");
    /**
     *  taskExpiredTime 过期时间 [long] 单位:ms
     */
    private Long taskExpiredTime = 10000l;
    /**
     * 截止时间 [String] [yyyy-MM-dd HH:mm:ss]
     */
    private String taskEndTime ="9999-01-01 01:01:01" ;
    /**
     * 最后一次执行时间戳
     */
    private Long lastExcTime = 0l;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public SchedulerTaskConf[] getConfs() {
        return confs;
    }

    public void setConfs(SchedulerTaskConf[] confs) {
        this.confs = confs;
    }

    public Long getInterval() {
        return interval;
    }

    public void setInterval(Long interval) {
        this.interval = interval;
    }

    public String getTaskStartTime() {
        return taskStartTime;
    }

    public void setTaskStartTime(String taskStartTime) {
        this.taskStartTime = taskStartTime;
    }

    public Long getTaskExpiredTime() {
        return taskExpiredTime;
    }

    public void setTaskExpiredTime(Long taskExpiredTime) {
        this.taskExpiredTime = taskExpiredTime;
    }

    public String getTaskEndTime() {
        return taskEndTime;
    }

    public void setTaskEndTime(String taskEndTime) {
        this.taskEndTime = taskEndTime;
    }

    public Long getLastExcTime() {
        return lastExcTime;
    }

    public void setLastExcTime(Long lastExcTime) {
        this.lastExcTime = lastExcTime;
    }

    public SchedulerTaskConf (String  taskStartTime ,Long taskExpiredTime,Long interval,String taskEndTime){
        //设置开始时间,格式 : yyyy-MM-dd HH:mm:ss
        this.setTaskStartTime(taskStartTime);
        //设置延时执行时间10s
        this.setTaskExpiredTime(taskExpiredTime);
        // 设置发送频率 20s
        this.setInterval(interval);
        //设置定时任务终止时间,格式 : yyyy-MM-dd HH:mm:ss
        if(taskEndTime != null ){
            this.setTaskEndTime(taskEndTime  );
        }
    }

    public Long  getFirst(){
        Long nowStamp =  System.currentTimeMillis();
        Long start=  DateUtil.stringToTimestamp(this.getTaskStartTime(),"").getTime();
        Long end ;
        if(StringUtils.isEmpty(this.getTaskEndTime())){
              end = Long.MAX_VALUE;
        }else {
              end = DateUtil.stringToTimestamp(this.getTaskEndTime(),"").getTime();
        }
        if(start+ taskExpiredTime < nowStamp){
            return nowStamp;
        }
        if(start+ taskExpiredTime >  end){
            return 0l;
        }
        Long myStart = start+ taskExpiredTime;
        if(this.getConfs()!= null ){
            SchedulerTaskConf[] confs = this.getConfs();
            for(SchedulerTaskConf  conf:confs){
                Long nodeStart =  conf.getFirst();
                if(nodeStart <= 0l){
                    continue;
                }
                if(myStart >  nodeStart){
                    myStart = nodeStart;
                }
            }
        }
        return myStart;
    }


    public Long  getNext(){
        Long nowStamp =  System.currentTimeMillis();
        //超过定时截止日期,则直接返回0
        if(!StringUtils.isEmpty(this.getTaskEndTime())    ){
            Long end = DateUtil.stringToTimestamp(this.getTaskEndTime(),"").getTime();
            if(end  < nowStamp ){
                return  0L ;
            }
        }
        if(this.getLastExcTime()< nowStamp ){
            this.setLastExcTime(nowStamp);
        }else {
            return this.getLastExcTime() ;
        }

        if(interval <=0 ){
            return  0l ;
        }
        Long next = this.getLastExcTime() + interval;
        if(this.getConfs()!= null ){
            SchedulerTaskConf[] confs = this.getConfs();
            for(SchedulerTaskConf  conf:confs){
                Long nodeNext =  conf.getNext();
                if(nodeNext <= 0l){
                    continue;
                }
                if(next >  nodeNext){
                    next = nodeNext;
                }
            }
        }
        if(next  < this.getLastExcTime() ){
            return  0l ;
        }
        return next;
    }
}

  6.

通过延时队列实现定时任务操作
public class SchedulerTaskHelper {

    private static final Logger logger = LoggerFactory.getLogger(SchedulerTaskHelper.class);


    private volatile static Map<Long,SchedulerTaskConf> confMap = new HashMap<Long,SchedulerTaskConf>();
    private volatile static Map<Long,SchedulerTask> taskMap = new HashMap<Long,SchedulerTask>();
    private static ThreadPool tp = new DefaultThreadPool(1);

    public static ThreadPool getThreadPool(){
        return tp;
    }
    /**
     * 任务生成并添加
     * @param schedulerTask
     * @param taskConf
     */
    public static void registryTask(SchedulerTask schedulerTask,SchedulerTaskConf taskConf){

        if(!taskMap.containsKey(schedulerTask.getId())){
            synchronized (confMap){
                if(!taskMap.containsKey(schedulerTask.getId())) {
                    taskMap.put(schedulerTask.getId(),schedulerTask);
                }
            }
        }
        if(confMap.containsKey(schedulerTask.getId())){
            logger.error("此任务已经存在定时计划");
//            System.out.println("此任务已经存在定时计划");
        }else {
            synchronized (confMap){
                if(!confMap.containsKey(schedulerTask.getId())){
                     confMap.put(schedulerTask.getId(),taskConf);
                }
            }
        }
        taskInit(schedulerTask,taskConf);
    }

    /**
     * 任务第一次添加队列
     * @param schedulerTask
     * @param taskConf
     */
    public static void taskInit(SchedulerTask schedulerTask,SchedulerTaskConf taskConf){
        DelayQueueHelper delayQueueHelper =   DelayQueueHelper.getInstance();
        DelayTask delayTask = new DelayTask();
        Long start =  taskConf.getFirst();
        if(start <= 0l){
            return;
        }
        Long now = System.currentTimeMillis();
        delayTask.initTaskTime(DateUtil.date2String(new Date(),""),start - now , schedulerTask.getId());
        delayQueueHelper.addTask(delayTask);
        logger.info("定时任务初始化成功: id = " + delayTask.gettId());
    }

    /**
     * 添加下一次执行任务到队列
     * @param schedulerTask
     * @param taskConf
     */
    public static void taskNextIn(SchedulerTask schedulerTask,SchedulerTaskConf taskConf){
        DelayQueueHelper delayQueueHelper =   DelayQueueHelper.getInstance();
        DelayTask delayTask = new DelayTask();
        Long next = taskConf.getNext();
//        MyLog.info(schedulerTask.getDescription() + "下次执行时间: " + DateUtil.stampToTime(next));
        if(next <= 0l){
            stop(schedulerTask   );
            return;
        }
        Long now = System.currentTimeMillis();

        delayTask.initTaskTime(DateUtil.date2String(new Date(),""),next - now , schedulerTask.getId());
//        if(delayTask.gettId() == 1l){
//            System.out.println("任务"+ JSONObject.toJSONString(delayTask));
//            System.out.println("1");
//        }
        MyLog.debug(schedulerTask.getDescription() + " 下次执行时间在: " + (next/1000 - now/1000 ) +"s 后");
//        MyLog.debug( "TaskStartTime: " + DateUtil.date2String(delayTask.getTaskStartTime(),"") +", TaskExpiredTime : "+  DateUtil.stampToTime(delayTask.getTaskExpiredTime()));
        delayQueueHelper.addTask(delayTask);
    }

    /**
     * 线程执行任务
     * @param  id
     */
    public static void  start(Long id ){
        SchedulerTaskConf conf = confMap.get(id);
        if(conf == null ){
            logger.error("定时任务时间配置丢失:"+ id);
        }

        SchedulerTask schedulerTask =  taskMap.get(id);
        if(schedulerTask == null ){
            logger.error("定时任务丢失:"+ schedulerTask.getDescription());
        }
        logger.info(" 执行定时任务 " +schedulerTask.getDescription() );
        taskNextIn(schedulerTask,conf);
        tp.execute(schedulerTask);
    }

    //获取任务类名
    public static String getClassName(Long id ){
        SchedulerTask task  =  taskMap.get(id);
        return task.getClass().getName();
    }

    //移除任务
    public static boolean removeTask(Long id  ){
        if(taskMap.containsKey(id)){
            taskMap.remove(id);
            return true;
        }
         return false;
     }

    public static boolean stop(SchedulerTask schedulerTask){
        if(schedulerTask.getStopCall() != null ){
            new Thread(schedulerTask.getStopCall()).start();
        }
       return removeTask(schedulerTask.getId()  );
    }

}

  7.

任务id生成器,每次+1  唯一性
public class TaskIdSequnce {
    static Long l = 0l ;

    public static Long creatId(){
        synchronized(l){
            //自增1
            l =    l +1l ;
            return l;
        }
    }
}

  8 .线程池接口

public interface ThreadPool<Job extends SchedulerTask> {
    void execute(Job job);
    void shutdown();
    void addWorkers(int num);
    void removeWorker(int num);
    int getJobSize();
}

  9.实现线程池

public class DefaultThreadPool<Job extends SchedulerTask>  implements ThreadPool<Job> {
    private static final Logger logger = LoggerFactory.getLogger(DefaultThreadPool.class);

    private    int MAX_WORKER_SIZE = 10 ;

    private    int DEFAULT_WORKER_SIZE = 5;

    private    int MIN_WORKER_SIZE = 5;

    private List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>());

    private final LinkedList<Job> jobs = new LinkedList<Job>();

    private AtomicLong threadNum = new AtomicLong();

    private int workerNum = DEFAULT_WORKER_SIZE;

    public void execute(Job job) {
        if(job!=null){
            synchronized (jobs){
                jobs.add(job);
                jobs.notify();
            }
        }
    }

    public void shutdown() {
        for(Worker  worker:workers){
            worker.shutdown();
        }

    }

    public void addWorkers(int num) {
        synchronized (jobs){
            if(num + workerNum> MAX_WORKER_SIZE){
                num = MAX_WORKER_SIZE- workerNum;
            }
            initializeWorkers(num);
            workerNum += num;
        }
    }

    public void removeWorker(int num) {
        synchronized (jobs){
            if(num > workerNum){
                try {
                    throw new IllegalAccessException("");
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            int count =0  ;
            while (count< num ){
                Worker worker = workers.get(count);
                if(workers.remove(worker)){
                    worker.shutdown();
                    count++;
                }
            }
            workerNum -= count;
        }
    }

    public int getJobSize() {
        return jobs.size();
    }

    public DefaultThreadPool( int MAX_WORKER_SIZE, int DEFAULT_WORKER_SIZE,int  MIN_WORKER_SIZE){
        this.MAX_WORKER_SIZE = MAX_WORKER_SIZE;
        this.DEFAULT_WORKER_SIZE = DEFAULT_WORKER_SIZE;
        this.MIN_WORKER_SIZE = MIN_WORKER_SIZE;
        initializeWorkers(DEFAULT_WORKER_SIZE);
    }

    public DefaultThreadPool(int num ){
        workerNum = num > MAX_WORKER_SIZE ?MAX_WORKER_SIZE:num< MIN_WORKER_SIZE?MIN_WORKER_SIZE:num ;
        initializeWorkers(workerNum);
    }

    private void initializeWorkers(int num){
        for(int i = 0 ; i < num; i ++ ){
            Worker worker = new Worker();
            workers.add(worker);
            Thread thread = new Thread(worker,"ThreadPool-Worker-"+ threadNum.incrementAndGet() );
            thread.start();

        }

    }

    class Worker implements Runnable {
        private  volatile  boolean running = true ;
        public void run() {
            while (running){
                Job job = null ;
                synchronized (jobs){
                    while(jobs.isEmpty()){
                        try {
                            jobs.wait();
                        }catch (Exception e){
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }
                    job = jobs.removeFirst();
                }
                if(job != null ){
                    try {
                        Thread td =Thread.currentThread();
                        try {
                            if(job.getId() != null ){
                                td.setName("Scheduler-Worker-" + threadNum.incrementAndGet() +"-"+ SchedulerTaskHelper.getClassName(job.getId()));
                            }else {
                                td.setName("Scheduler-Worker-" + threadNum.incrementAndGet() +"-" );
                            }
                        }catch (Exception e){
                        }
                        if(job.getBeforeCall()!= null ){
                            try {
                                job.getBeforeCall().run(); ;
                            }catch (Exception e){
                                logger.error("线程池前调错误");
                            }
                        }
                        job.run();
                        if(job.getAfterCall() != null ){
                            try {
                                if(job.getDelayTime()> 0 ){
                                    Object waitO  = new Object();
                                    synchronized (waitO){
                                        waitO.wait(job.getDelayTime());
                                    }
//                                    Thread.sleep();
                                }
                                job.getAfterCall().run();
                            }catch (Exception e){
                                logger.error("线程池后调错误"+ ExceptionUtil.getMessage(e));
                            }
                        }
                    }catch (Exception e){

                    }
                }

            }
        }

        public void  shutdown(){
            running = false ;
        }
    }
}

  

posted @ 2022-10-26 17:07  higsan  阅读(102)  评论(0编辑  收藏  举报