延时队列实现自定义定时服务
1.延时队列元素定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 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.实现延时队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 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.定义任务接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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.抽象任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 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.定时任务执行时间配置表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | 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.
通过延时队列实现定时任务操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | 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 唯一性
1 2 3 4 5 6 7 8 9 10 11 | public class TaskIdSequnce { static Long l = 0l ; public static Long creatId(){ synchronized (l){ //自增1 l = l +1l ; return l; } } } |
8 .线程池接口
1 2 3 4 5 6 7 | public interface ThreadPool<Job extends SchedulerTask> { void execute(Job job); void shutdown(); void addWorkers( int num); void removeWorker( int num); int getJobSize(); } |
9.实现线程池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | 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 ; } } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏