Java 本地延迟队列DelayQueue的实现




项目背景

最近做的项目有个需求:生成一则工单推送给用户后,等待用户处理。15分钟后再查询工单的状态,如果超时未处理,则推送一则超时工单消息通知用户。

实现的方式很多,这里不做讨论。由于项目规模不是很大,这里不想引入MQ中间件,增加系统复杂度。所以用JDK 提供的 DelayQueue 实现了一把延迟队列 ~~ 


代码实现

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * @author huanghaihua
 * @since 2021/11/27
 * @description 延迟实体
 */
public class DelayedItem<K> implements Delayed {

    /** key **/
    private K t;
    /** 延迟时间 **/
    private long liveTime;
    /** 移除的具体时间 **/
    private long removeTime;

    public DelayedItem(K t, long liveTime) {
        this.setK(t);
        this.liveTime = liveTime;
        this.removeTime = TimeUnit.NANOSECONDS.convert(liveTime, TimeUnit.MILLISECONDS) + System.nanoTime();
    }

    @Override
    public long getDelay(TimeUnit unit) {
        long i = unit.convert(removeTime - System.nanoTime(), unit);
        return i;
    }

    @Override
    public int compareTo(Delayed o) {
        if (o == null)
            return 1;
        if (o == this)
            return 0;
        if (o instanceof DelayedItem) {
            DelayedItem<K> tmpDelayedItem = (DelayedItem<K>) o;
            if (removeTime > tmpDelayedItem.removeTime) {
                return 1;
            } else if (removeTime == tmpDelayedItem.removeTime) {
                return 0;
            } else {
                return -1;
            }
        }
        long diff = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        return diff > 0 ? 1 : diff == 0 ? 0 : -1;
    }

    public K getK() {
        return t;
    }

    public void setK(K t) {
        this.t = t;
    }

    public int hashCode() {
        return t.hashCode();
    }

    public boolean equals(Object object) {
        if (object instanceof DelayedItem) {
            return object.hashCode() == hashCode() ? true : false;
        }
        return false;
    }
}

import java.util.Objects;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author huanghaihua
 * @since 2021/11/27
 * @description 延迟缓存
 */
@Slf4j
public abstract class DelayCache<K> {

	public DelayQueue<DelayedItem<K>> queue = new DelayQueue<>();


	// 启动10个线程去查询 防止阻塞
	ExecutorService cachedThreadPool = Executors.newFixedThreadPool(10);

	/**
	 * put: 延迟队列
	 * 
	 * @param k
	 *            key
	 * @param liveTime
	 *            延迟时间 单位毫秒 异常: @exception 
	 */
	public void put(K k, long liveTime) {
		log.info(">>>[put]>>>put key:" + k + " into delayQueue cache!");
		DelayedItem<K> tmpItem = new DelayedItem<>(k, liveTime);
		queue.put(tmpItem);
	}

	public DelayCache() {
		Thread t = new Thread(() -> {
			try {
				dameonCheckOverdueKey();
			} catch (InterruptedException e) {
				log.error(e.getMessage(),e);
			}
		});
		/**
		 * setDaemon():
		 * 程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成
		 * 。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是,只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,
		 * 这时就可以用setDaemon方法了。
		 */
		t.setDaemon(true);
		t.start();
	}

	public void dameonCheckOverdueKey() throws InterruptedException {
		while (true) {
			DelayedItem<K> delayedItem = queue.take();
			if (Objects.nonNull(delayedItem)) {
				log.info(">>>>[dameonCheckOverdueKey]>>> get key:" + delayedItem.getK() + " from delayQueue cache!");
				cachedThreadPool.execute(() -> invoke(delayedItem.getK()));
			}
		}
	}

	public abstract void invoke(K k);

}
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.IntStream;


/**
 * @author huanghaihua
 * @since 2021/11/27
 * @description 延迟调用缓存队列
 */
@Slf4j
public class LocalCacheDelay extends DelayCache<String> {

	public static final LocalCacheDelay service = new LocalCacheDelay();

	/**
	 * put: 延迟队列
	 * @param oderKey
     *            K
	 * @param liveTime
	 *            延迟时间 单位毫秒
	 */
	public void put(String oderKey, long liveTime) {
		super.put(oderKey, liveTime);
	}

	public void invoke(String k) {
		try {
			log.info(">>>>>[invoke]>>>> receive key: {}",k);
		} catch (Exception e) {
			log.error(e.getMessage(),e);
		}
	}

    /**
	 * 模仿测试用,生成可选择去掉
	 */

	public static void main(String[] args) throws InterruptedException {
		//构造100w个用户
		List<String> users = new ArrayList<>(1000000);
		IntStream.range(0, 1000000).parallel().forEach(b -> users.add("路人乙:" + b));

		//并行流执行 ,15分钟
		users.parallelStream().forEach(u -> LocalCacheDelay.service.put(u,900000));
	
	}


}

运行结果

15分钟前:  

 经过漫长的等待,15分钟后:

 

如有问题,请留言指导修正~ emememmmm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值