简介
DelayQueue
Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部 是延迟期满后保存时间最长的Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于 0 的值时,将发生到期。即使无法使用take 或 poll 移除未到期的元素,也不会将这些元素作为正常元素对待。例如,size方法同时返回到期和未到期元素的计数。此队列不允许使用 null 元素。
DelayQueue类图结构
在源码中看到 DelayQueue中内部使用的是PriorityQueue存放数据,使用ReentrantLock实现线程同步,可知是阻塞队列;
public class DelayQueue extends AbstractQueue
implements BlockingQueue {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue q = new PriorityQueue();
元素实例
package com.cr.core.delay.entity;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
*
* 订单超时实体类
* Created by li on 2018/5/18.
*/
public class Order implements Delayed {
/**
* 订单号
*/
private long orderId;
/**
* 开始执行时间
*/
private long startTime;
public Order(){
}
/**
* orderId:订单id
* timeout:订单超时时间,秒
* */
public Order(long orderId, int timeout){
this.orderId = orderId;
this.startTime = System.currentTimeMillis() + timeout*1000L;
}
/**
* 返回当前对象的剩余延迟时间
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(startTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
/**
* 比较当前对象与指定对象的顺序(出栈顺序)
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
if (o == this){
return 0;
}
if(o instanceof Order){
Order otherRequest = (Order)o;
long otherStartTime = otherRequest.getStartTime();
return (int)(this.startTime - otherStartTime);
}
return 0;
}
public long getOrderId() {
return orderId;
}
public void setOrderId(long orderId) {
this.orderId = orderId;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
@Override
public String toString() {
return "DSHOrder{" +
"orderId=" + orderId +
", startTime=" + startTime +
'}';
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}
应用实例
package com.cr.core.delay.service;
import com.cr.core.delay.entity.Order;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import java.util.concurrent.DelayQueue;
/**
* Created by li on 2018/5/18.
*/
@Service
public class DelayService {
private static final Logger log = Logger.getLogger(DelayService.class);
private boolean start ;
private DelayedListener listener;
private DelayQueue delayQueue = new DelayQueue();
//内部接口,监听器启动调本类start接口需要实现
public interface DelayedListener{
void delayedListener(Order order);
}
public void start(DelayedListener listener){
if(start){
return;
}
log.error("DelayService 启动");
start = true;
this.listener = listener;
new Thread(()->{
try{
while(true){
Order order = delayQueue.take();
if(DelayService.this.listener != null){
DelayService.this.listener.delayedListener(order);
}
}
}catch(Exception e){
log.info(e.getMessage(),e);
}
}).start();
}
public void add(Order order){
delayQueue.put(order);
}
public boolean remove(Order order){
return delayQueue.remove(order);
}
public void add(long orderId){
delayQueue.put(new Order(orderId,24*3600*1000));
}
public void remove(long orderId){
Order[] array = delayQueue.toArray(new Order[]{});
if(array == null || array.length <= 0){
return;
}
Order target = null;
for(Order order : array){
if(order.getOrderId() == orderId){
target = order;
break;
}
}
if(target != null){
delayQueue.remove(target);
}
}
}
监听器实例
package com.cr.core.delay.service;
import com.cr.core.delay.entity.Order;
import com.cr.core.delay.entity.ThreadPoolUtil;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import java.util.List;
/**
* Created by li on 2018/5/18.
*/
public class StartupListener implements ApplicationListener {
private static final Logger log = Logger.getLogger(StartupListener.class);
@Autowired
DelayService delayService;
@Override
public void onApplicationEvent(ApplicationEvent evt) {
if (evt.getSource() == null) {
return;
}
//自动收货
delayService.start((Order order)->{
//异步来做
ThreadPoolUtil.execute(()->{
long orderId = order.getOrderId();
//查库判断是否需要自动收货
log.error("自动确认收货,onDelayedArrived():"+orderId);
//从redis删除
//redisService.delete(Constants.RedisKey.DSH_PREFIX+orderId, RedisService.DB.DSH);
log.error("自动确认收货,删除redis:"+orderId);
});
});
//查找需要入队的订单
ThreadPoolUtil.execute(()->{
log.error("查找需要入队的订单");
//扫描redis,找到所有可能的orderId
List keys = null;//redisService.scan(RedisService.DB.DSH);
if(keys == null || keys.size() <= 0){
return;
}
log.error("需要入队的订单keys:"+keys);
//写到DelayQueue
for(String key : keys){
Order order = null;//redisService.get(key, DSHOrder.class, RedisService.DB.DSH);
log.error("读redis,key:"+key);
if(order != null){
//delayService.add(order);
log.error("订单自动入队:"+order.getOrderId());
}
}
});
}
}
使用场景
1、做订单超时、支付超时、和第三方交互时幂等重试等等;