JDK-DelayQueue实现的延迟队列
延迟元素的无限制BlockingQueu,其中元素只能在其延迟到期后才能获取。当元素的getDelay(TimeUnit.NANOSECONDS)方法返回小于或等于零的值时,就会发生过期。即使未到期的元素无法使用take或poll删除,它们也被视为普通元素。此队列不允许出现null 元素。
业务处理类(DelayTask)实现Delayed接口
public class DelayTask implements Delayed {
private static long currentTime = System.currentTimeMillis();
// 任务名称
protected final String taskName;
// 处理任务时间
protected final int timeCost;
// 任务过期时间 (我设置的1分钟+随机数,防止任务冲突在同一时间)
protected final long scheduleTime;
// 记录当前任务数量
protected static final AtomicInteger taskCount = new AtomicInteger(0);
// 定时任务之间的启动时间间隔在1~2s之间,timeCost表示处理此任务需要的时间,本示例中为2s
public DelayTask(String taskName, int timeCost) {
this.taskName = taskName;
this.timeCost = timeCost;
taskCount.incrementAndGet();
currentTime += 60000 + (long) (Math.random() * 1000);
scheduleTime = currentTime;
}
/**
* 在你put方法时,会调用该方法,该方法是获取,或者设置任务过期时间
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
return (int) (this.scheduleTime - ((DelayTask) o).scheduleTime);
}
/**
* 在你调用take()方法时调用, 该方法是用任务时间减去当前时间,判断任务是否过期。
* 在业务可以判定自己返回0还是-1
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
long expirationTime = scheduleTime - System.currentTimeMillis();
return unit.convert(expirationTime, TimeUnit.MILLISECONDS);
}
/**
* 业务处理类
*/
public void execTask() {
long startTime = System.currentTimeMillis();
System.out.println("Task " + taskName + ": schedule_start_time=" + scheduleTime + ",real start time="
+ startTime + ",delay=" + (startTime - scheduleTime));
try {
Thread.sleep(timeCost);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
调用线程执行方法
public class DelayTaskComsumer extends Thread {
private final BlockingQueue<DelayTask> queue;
public DelayTaskComsumer(BlockingQueue<DelayTask> queue) {
this.queue = queue;
}
@Override
public void run() {
System.out.println("开始执行任务");
DelayTask task = null;
try {
while (true) {
System.out.println("正在执行任务");
// 查询当前在对头任务是否快要过期
task = queue.take();
// 调用你执行的业务方法
task.execTask();
// 删除对应记录任务
DelayTask.taskCount.decrementAndGet();
}
} catch (InterruptedException e) {
System.out.println(getName() + " finished");
}
}
}
测试类
public class DelayQueueExample {
public static void main(String[] args) {
//阻塞队列 父类都是 Queue 实现了 Collection接口
BlockingQueue<DelayTask> queue = new DelayQueue<DelayTask>();
// 创建 10个任务进行入队
for (int i = 0; i < 10; i++) {
try {
// 入队, 设置任务处理时间时间两秒
queue.put(new DelayTask("work " + i, 2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 创建一个线程组
ThreadGroup g = new ThreadGroup("Consumers");
//创建启动线程
for (int i = 0; i < 1; i++) {
new Thread(g, new DelayTaskComsumer(queue)).start();
}
while (DelayTask.taskCount.get() > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
g.interrupt();
System.out.println("Main thread finished");
}
}
重点 take()方法解析
public E take() throws InterruptedException {
// 获取锁
final ReentrantLock lock = this.lock;
// 可中断锁
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
// 如果第一个元素为 null,则阻塞
if (first == null){
available.await();
} else {
// 获取剩余时间,回调你重写的getDelay()方法
long delay = first.getDelay(NANOSECONDS);
// 等待时间到了,直接执行 poll 返回元素。
if (delay <= 0){
return q.poll();
}
first = null;
// 当前有其他线程再处理,则进入等待。
if (leader != null){
available.await();
} else {
// 设置 leader 为当前线程
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
// 执行等待
available.awaitNanos(delay);
} finally {
// 释放 leader 信息
if (leader == thisThread){
leader = null;
}
}
}
}
}
} finally {
// 当前线程出队完成,通知其他出队阻塞的线程继续执行,并释放锁
}
总结:
如果用于在取消订单中
1. 服务器重启数据会全部丢失
2. 当订单数量过多会出现OOM异常现象