使用delayedQueue实现你本地的延迟队列

来源:互联网 发布:阿里云市场 编辑:程序博客网 时间:2024/04/30 23:56
    • 了解DelayQueue
      • DelayQueue是什么
      • DelayQueue能做什么
    • 怎么用DelayQueue来解决这类的问题
      • 先声明一个Delayed的对象
      • 再实现一个管理延迟任务的类
      • 使用方法

了解DelayQueue

DelayQueue是什么?

DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。

DelayQueue能做什么?

在我们的业务中通常会有一些需求是这样的:
1. 淘宝订单业务:下单之后如果三十分钟之内没有付款就自动取消订单
2. 饿了吗订餐通知:下单成功后60s之后给用户发送短信通知

那么这类业务我们可以总结出一个特点:需要延迟工作。
由此的情况,就是我们的DelayQueue应用需求的产生。

怎么用DelayQueue来解决这类的问题

先声明一个Delayed的对象

import java.util.concurrent.Delayed;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicLong;/** * <p> * [任务调度系统] * <br> * [队列中要执行的任务] * </p> * * @author wangguangdong * @version 1.0 * @Date 2015年11月22日19:46:39 */public class Task<T extends Runnable> implements Delayed {    /**     * 到期时间     */    private final long time;    /**     * 问题对象     */    private final T task;    private static final AtomicLong atomic = new AtomicLong(0);    private final long n;    public Task(long timeout, T t) {        this.time = System.nanoTime() + timeout;        this.task = t;        this.n = atomic.getAndIncrement();    }    /**     * 返回与此对象相关的剩余延迟时间,以给定的时间单位表示     */    @Override    public long getDelay(TimeUnit unit) {        return unit.convert(this.time - System.nanoTime(), TimeUnit.NANOSECONDS);    }    @Override    public int compareTo(Delayed other) {        // TODO Auto-generated method stub        if (other == this) // compare zero ONLY if same object            return 0;        if (other instanceof Task) {            Task x = (Task) other;            long diff = time - x.time;            if (diff < 0)                return -1;            else if (diff > 0)                return 1;            else if (n < x.n)                return -1;            else                return 1;        }        long d = (getDelay(TimeUnit.NANOSECONDS) - other.getDelay(TimeUnit.NANOSECONDS));        return (d == 0) ? 0 : ((d < 0) ? -1 : 1);    }    public T getTask() {        return this.task;    }    @Override    public int hashCode() {        return task.hashCode();    }    @Override    public boolean equals(Object object) {        if (object instanceof Task) {            return object.hashCode() == hashCode() ? true : false;        }        return false;    }}
  • 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

再实现一个管理延迟任务的类

import org.apache.log4j.Logger;import java.util.concurrent.DelayQueue;import java.util.concurrent.Executor;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;/** * <p> * [任务调度系统] * <br> * [后台守护线程不断的执行检测工作] * </p> * * @author wangguangdong * @version 1.0 * @Date 2015年11月23日14:19:40 */public class TaskQueueDaemonThread {    private static final Logger LOG = Logger.getLogger(TaskQueueDaemonThread.class);    private TaskQueueDaemonThread() {    }    private static class LazyHolder {        private static TaskQueueDaemonThread taskQueueDaemonThread = new TaskQueueDaemonThread();    }    public static TaskQueueDaemonThread getInstance() {        return LazyHolder.taskQueueDaemonThread;    }    Executor executor = Executors.newFixedThreadPool(20);    /**     * 守护线程     */    private Thread daemonThread;    /**     * 初始化守护线程     */    public void init() {        daemonThread = new Thread(() -> execute());        daemonThread.setDaemon(true);        daemonThread.setName("Task Queue Daemon Thread");        daemonThread.start();    }    private void execute() {        System.out.println("start:" + System.currentTimeMillis());        while (true) {            try {                //从延迟队列中取值,如果没有对象过期则队列一直等待,                Task t1 = t.take();                if (t1 != null) {                    //修改问题的状态                    Runnable task = t1.getTask();                    if (task == null) {                        continue;                    }                    executor.execute(task);                    LOG.info("[at task:" + task + "]   [Time:" + System.currentTimeMillis() + "]");                }            } catch (Exception e) {                e.printStackTrace();                break;            }        }    }    /**     * 创建一个最初为空的新 DelayQueue     */    private DelayQueue<Task> t = new DelayQueue<>();    /**     * 添加任务,     * time 延迟时间     * task 任务     * 用户为问题设置延迟时间     */    public void put(long time, Runnable task) {        //转换成ns        long nanoTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.MILLISECONDS);        //创建一个任务        Task k = new Task(nanoTime, task);        //将任务放在延迟的队列中        t.put(k);    }    /**     * 结束订单     * @param task     */    public boolean endTask(Task<Runnable> task){        return t.remove(task);    }}
  • 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

使用方法

  1. 在容器初始化的时候调用init方法.
  2. 实现一个runnable接口的类,调用TaskQueueDaemonThread的put方法传入进去.
  3. 如果需要实现动态的取消任务的话,需要task任务的类重新hashcode方法,最好用业务限制hashcode的冲突发生.