赞
踩
延迟任务在工作的业务中有很多的应用场景,比如下订单后监听是否支付成功,若多少分钟后未支付则自动取消订单;比如注册用户后,再三分钟后提醒用户完成某项操作等,应用的场景很多,同时实现的方式也很多,在这里我引入一个我看过的博客,使用Redis实现延时任务(lua语言),写的应该还是可以的。
在我这篇博客里,介绍三种实现延时任务的方式,它们的优劣点可以看一下这个图吧,总结的很好
这三种分别是JDK的延时队列、Redis、Rabbitmq,这里解释一下为什么没有介绍另外两种,因为基于数据库的方式在现在的项目中,尤其是大数据的项目中,很难用到。第二种我对其中的技术不太了解,不好误人子弟。
好了,正式开始吧
延时队列DelayQueue是JDK1.5提出来的,当前在这之前我不太了解这个类,这个也是我的薄弱点,看一下官方的解释吧
这块的解释吧,先用网上比较普遍的总结,那就是DelayQueue即具备队列的功能,又具备延时的功能。重点点说,可以将元素放到DelayQueue中,并且只有元素到期才可以取出来,如何判断元素到期呢?其实这个地方就要注意了,继承实现Delayed的时候需要去重写一个方法,那就是getDelay,当这个getDelay返回是0或者比0小的时候,则认为任务过期了,可以拿出来。
从DelayQueue取出任务的方式呢,就是take(),这个方法也就有解释的,就是当任务过期才可以取出来了,如果没有任务过期,则阻塞当前线程。还有一些其他的比如不可以存储null等等,这些就不解释了,最重要的就是take以及getDelay
接下来看一下我写的例子吧
/** * @program: delayTasks * @description: 实际业务执行类 * @author: hs * @create: 2021-01-10 20:14 **/ public class DelayTask implements Runnable{ private String taskName; public DelayTask(String name){ taskName = name; } @Override public void run() { System.out.println("********START************"); System.out.println("当前线程名:" + Thread.currentThread().getName()); System.out.println("当前任务名:" + taskName); System.out.println("实际业务执行位置"); System.out.println("********END************"); } }
/** * @program: delayTasks * @description: 延时队列中消息体,对任务进行了封装 * @author: hs * @create: 2021-01-10 20:17 **/ public class DelayMessage<T extends Runnable> implements Delayed { private final Long time; private final T task; public DelayMessage(Long time, T task) { this.time = System.nanoTime()+time; this.task = task; } public Long getTime() { return time; } public T getTask() { return task; } /** * 根据给定的时间单位,给出当前任务的剩余延迟时间 * @param unit TimeUnit * @return 返回剩余延迟时间 */ @Override public long getDelay(TimeUnit unit) { return unit.convert(this.time-System.nanoTime(),TimeUnit.NANOSECONDS); } /** * 比较方法,未到期返回1,消息到期返回0或-1 * @param o * @return */ @Override public int compareTo(Delayed o) { DelayMessage other = (DelayMessage) o; Long diff = this.time-other.time; if(diff>0){ return 1; }else if(diff<0){ return -1; } return 0; } }
/** * @program: delayTasks * @description: 自定义延迟队列管理类 * @author: hs * @create: 2021-01-13 20:09 **/ public class DelayQueueCostomizedManager { /** * 核心线程,长期保持的线程数——自定义线程池所用参数 */ private final static Integer COREPOOLSIZE = 10; /** * 最大线程数——自定义线程池所用参数 */ private final static Integer MAXIMUMPOOLSIZE = 100; /** * 闲置线程存活时间——自定义线程池所用参数 */ private final static Long KEEPALIVETIME = 50L; /** * 线程池执行器 */ private ExecutorService executorService; /** * 守护线程,由此线程去维护执行延时队列操作 */ private Thread daemonThread; /** * 延时队列存放位置 */ private DelayQueue<DelayMessage<?>> delayQueue; /** * 单例模式——饿汉模式 */ private static DelayQueueCostomizedManager instance = new DelayQueueCostomizedManager(); private DelayQueueCostomizedManager(){ // 创建线程池执行器 executorService = new ThreadPoolExecutor(COREPOOLSIZE,MAXIMUMPOOLSIZE,KEEPALIVETIME, TimeUnit.MILLISECONDS,new LinkedBlockingDeque<Runnable>(),new ThreadPoolExecutor.AbortPolicy()); // 初始化延迟队列 delayQueue = new DelayQueue<DelayMessage<?>>(); init(); } public void init(){ // 自启后,监听延迟队列 daemonThread = new Thread(() -> { execute(); }); daemonThread.setName("守护线程,以此线程去执行"); // 因为是测试,所以将设置守护线程注释掉,否则main线程结束,守护线程也随之关闭,这样就看不到延迟队列的效果了 // daemonThread.setDaemon(true); daemonThread.start(); } // 单例,饿汉模式 public static DelayQueueCostomizedManager getInstance(){ return instance; } /** * 监听延迟队列 */ private void execute(){ while (true){ // 获得当前进程中线程痕迹 Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); System.out.println("当前存活线程数量:" + map.size()); System.out.println("当前延时任务数量:" + delayQueue.size()); try { // 获得延迟队列中的消息,必须消息到期才可以被获取,否则等待 DelayMessag
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。