用户购票后,产生未支付订单,系统30分钟后未支付自动取消。(每个订单放入线程中设置超时时间,自动处理)
本文 用一个线程 来循环跑对列,取消队列中的数据!!!
业务实现思路:
用户下单时,把当前订单放入队列中 设置过期时间,
知识回顾:
队列(Queue):(https://blog.csdn.net/zhongguozhichuang/article/details/53196415)
BlockingQueue(组塞线程):https://www.cnblogs.com/KingIceMou/p/8075343.html
ExecutorService(线程池):点击打开链接
本文对线程的基本概念也需要理解!
// =========================================准备工作
DelayQueue: 准备前缀 创建队列对象 点击打开链接 (https://blog.csdn.net/u012859681/article/details/77836139)
/**
* 实现 Delayed 类 重写 方法
*/
public class OrderItem implements Delayed {
private final long delayTime; // 延迟时间
private final long expire; // 到期时间
private Object data; // 数据
public OrderItem(){
delayTime=0;
expire=0;
}
public OrderItem(long delay, Object data) {
delayTime = delay;
this.data = data;
expire = System.currentTimeMillis() + delay;
}
/**
* 优先队列里面优先级规则 TimeUnit .MILLISECONDS 获取单位 为毫秒的时间戳
*/
@Override
public int compareTo(Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
/**
* 剩余时间=到期时间-当前时间 convert: 将给定单元的时间段转换到此单元。
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("DelayedElement{");
sb.append("delay=").append(delayTime);
sb.append(", expire=").append(expire);
sb.append(", data='").append(data.toString()).append('\'');
sb.append('}');
return sb.toString();
}
public Object getData() {
return data;
}
}
// ======================================================util类取消下单
first One: 创建队列util类
// 队列线程
public class ItemQueueThread {
public static DelayQueue delayQueue = new DelayQueue();
public static Thread thread;
/**
* flag=0:当前线程进入等待,flag=1当前线程可以竞争cpu使用权
*/
public static String flag="1";
public static void cancelOrder() {
thread = new Thread(new Runnable() {
@SuppressWarnings("null")
public void run() {
Thread.currentThread().setName("30分钟未支付自动取消run");
try {
synchronized (delayQueue) {
if (delayQueue.size() <= 0) {
logger.info("当前线程进入等待");
flag="0"; // 等待
delayQueue.wait(5*60*1000);
}else{
TicketOrderService orderService = (TicketOrderService) SpringContextUtil
.getBean(TicketOrderService.class);
Map map = Thread.getAllStackTraces();
logger.info("线程数--------" + map.size() + "延迟队列中数据元素:" + delayQueue.size());
OrderItem item = null;
try {
item = delayQueue.take(); // 队列取值。没有的时候等待
} catch (InterruptedException e) {
TicketOrder order = (TicketOrder) item.getData();
logger.error("定时取消订单失败,异常:" + e + "\n订单id" + order.getOrderId());
}
if (item != null) {
TicketOrder ticketOrder = (TicketOrder) item.getData();
ticketOrder.setOrderId(ticketOrder.getOrderId());
ticketOrder.setOrderStatus(OrderStatusEnum.zero.getValue());
ticketOrder.setCancelNumber(6);
ticketOrder.setCancelReason("支付超时");
String result = orderService.update(ticketOrder, null);
logger.info(" ticketOrder" + JSON.toJSONString(ticketOrder)+"自动取消结果:"+result);
try {
cancelOrder(item);// 取消订单 方法,(此方法为业务方法不予展示)
} catch (IOException | TemplateException | JSONException e) {
logger.error("定时取消订单失败,异常:" + e);
}
logger.info("过期时间:" + item.toString());
}
}
run(); // 循环调用一个线程跑队列
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
thread.start();
}
/**
* 下单成功的情况下取消订单,唤醒取消订单线程
*/
public static void threadNotify() {
if("0".equals(flag)){
synchronized (delayQueue) {
if("0".equals(flag)){
flag="1";
delayQueue.notify();
}
}
}
}
// ===============================start下单放入线程=============
start: 用户 进入 购票页面 购票 放入线程监控
public Map insert(HttpServletRequest request,HttpServletResponse response,Model model,TicketOrder ticketOrder) {
DelayQueue items=ItemQueueThread.delayQueue;
Map map1 = Thread.getAllStackTraces();
logger.info("线程数--------------" + map1.size());
// map=orderService.insert(ticketOrder, token); 录入订单
int num=(int) map.get("result");
if(num>0){
items.offer(new OrderItem(30*60*1000,ticketOrder));
ItemQueueThread.threadNotify();
map.put("status", "000");//下单成功
//下单成功,将成功订单信息存入缓存,设置过期时间30*60 秒 放入redis 缓存 id和时间 这里不做介绍
myRedisService.set(ticketOrder.getOrderId(), JSON.toJSONString(ticketOrder));
myRedisService.expire(ticketOrder.getOrderId(), 30*60);
}else{
items.offer(new OrderItem(1*1000,ticketOrder));
ItemQueueThread.threadNotify();
map.put("status", "001");//下单失败 ,直接取消订单
}
}else{
if("14001".equals(resultMap.get("code"))){
map.put("status", "004");//身份证号码不正确
}else{
map.put("status","001");//下单失败
}
}
return map;
}
// 项目启动时查询所有未支付订单 放入队列中 ,初始化队列
public class InitData implements ApplicationRunner {
@Autowired
private MyRedisService myRedisService;
@Autowired
private ParameterInfoService parameterService;
@Autowired
private TicketOrderService ticketOrderService;
@SuppressWarnings("unchecked")
@Override
public void run(ApplicationArguments args) throws Exception {
//获取所有超时未支付订单
DelayQueue items= ItemQueueThread.delayQueue;
List orders=ticketOrderService.selectTimeoutUnpaid(null);
if(orders!=null && orders.size()>0){
for (int i = 0; i < orders.size(); i++) {
int jsonInfo=Integer.parseInt(orders.get(i).getJsonInfo());
if(jsonInfo>0){
items.offer(new OrderItem(jsonInfo*1000,orders.get(i)));
myRedisService.set(orders.get(i).getOrderId(), JSON.toJSONString(orders.get(i)));
myRedisService.expire(orders.get(i).getOrderId(), jsonInfo);
}else{
items.offer(new OrderItem(i*1000,orders.get(i)));
}
}
}
ItemQueueThread.cancelOrder();
}
}