延迟队列、CacheLoader和阻塞队列

一.引言

今天重读了下之前接触的项目中的代码,略有感悟。主要是几种数据结构或组件的使用和几种设计模式,数据结构的话像google的CacheLoader、延迟队列、阻塞队列等;设计模式的话像单例模式、策略模式、委托模式、管道模式等等。本文主要记录几个数据结构的使用。

二.延迟队列

  • 延迟任务

    public class DelayTask<T extends Runnable> implements Delayed {
        private final long time;
        private final T task;
    
        public DelayTask(long time, T task){
            this.time = time;
            this.task = task;
        }
    
      	
      当getdelay结果为0时才能被取出
        @Override
        public long getDelay(@NotNull TimeUnit unit) {
            return unit.convert(this.time-System.nanoTime(), TimeUnit.NANOSECONDS);
        }
    
      
      取出任务时按照队列内部时间大小排序,时间越小的优先被取出
        @Override
        public int compareTo(@NotNull Delayed o) {
            DelayTask other = (DelayTask) o;
            long diff = time - other.time;
            if(diff>0){
                return 1;
            }else if(diff<0){
                return -1;
            }else {
                return 0;
            }
        }
    
        public T getTask(){
            return this.task;
        }
    }
    
  • 任务

    public class DelayWorker implements Runnable{
        private String content;
    
        public DelayWorker(String content){
            this.content = content;
        }
    
        @Override
        public void run() {
            System.out.println(this.content);
        }
    }
    
  • 管理器

    public class CacheManager {
        private final static int DEFAULT_THREAD_NUM = 4;
        private ExecutorService executor;
        private Thread daemonThread;
    
        private DelayQueue<DelayTask<?>> delayQueue;
        private static CacheManager cacheManager = new CacheManager();
    
        public static CacheManager getInstance(){
            return cacheManager;
        }
    
        private CacheManager(){
            executor = Executors.newFixedThreadPool(DEFAULT_THREAD_NUM);
            delayQueue = new DelayQueue<>();
            init();
        }
    
    
        public void init(){
            daemonThread = new Thread(() -> {
                execute();
            });
            daemonThread.start();
        }
    
        public void execute(){
            while (true){
                Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
                System.out.println("当前活跃线程数量为:" + allStackTraces.size());
                System.out.println("当前延迟任务数量为:" + delayQueue.size());
                try {
                    DelayTask delayTask = delayQueue.take();
                    if(delayTask != null){
                        Runnable task = delayTask.getTask();
                        if(task != null){
                            executor.execute(task);
                        }
                    }
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void put(Runnable task, long time, TimeUnit unit){
            long timeout = TimeUnit.NANOSECONDS.convert(time,unit);
            DelayTask<?> delayTask = new DelayTask<>(timeout,task);
            delayQueue.put(delayTask);
        }
    
    }
    
  • 测试

    public class GuavaCache {
        public static void main(String[] args) {
            CacheManager cacheManager = CacheManager.getInstance();
            cacheManager.put(new DelayWorker("任务"),1000, TimeUnit.MILLISECONDS);
        }
    }
    
  • 输出

    当前活跃线程数量为:6
    当前延迟任务数量为:0
    我的任务
    

    这里注意,明明任务被取出执行了,当前延时任务的数量为什么会是0,不应该是1吗?

    • 启东时是先执行的CacheManager的构造方法,由此进入了while循环,执行时还么有向队列中添加延时任务,因此第一次打印的延时任务数量为0,执行到delayqueue的时候发生了阻塞,等待队列元素
    • 向队列中添加延时任务后,take方法阻塞结束,取出延时任务执行

三.guava缓存

  • 配置缓存
  • 通过get获取key对应的value
  • 如果缓存中没有该key的话通过load方法计算并添加到缓存中
public class MyGuavaCache extends CacheLoader<Integer,Boolean> {

    private LoadingCache<Integer,Boolean> build = CacheBuilder.newBuilder()
            .maximumSize(10000)
            .expireAfterAccess(60, TimeUnit.MINUTES)
            .build(this);

    @Override
    public Boolean load(Integer num) throws Exception {
        if(num % 2 == 1){
            return true;
        }else{
            return false;
        }
    }


    public static void main(String[] args) throws ExecutionException {
        MyGuavaCache cache = new MyGuavaCache();
        System.out.println(cache.build.get(3));
        System.out.println(cache.build.get(4));
    }
}

四.阻塞队列

项目中使用的说是阻塞队列实际上更像是阻塞生产者,防止队列产生堆积

public class MyLinkedQueue<T> extends ConcurrentLinkedDeque {
    private int limit = 10;
    static final float load_factor = 0.5f;
    private Object lock = new Object();
    private static AtomicLong lockNum = new AtomicLong(0);

    public void put(String str){
        if(this.size() < limit){
            this.add(str);
        }else{
            //队列元素超上限时,生产者进入等待状态
            synchronized (lock){
                try {
                    lock.wait();
                    lockNum.addAndGet(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public T poll(){
        //队列有空闲时唤醒生产者
        if(limit * load_factor > this.size()){
            System.out.println("当前队列数量:" + this.size() + "解锁线程数量:" + lockNum.get());
            if(lockNum.get() > 0){
                synchronized (lock){
                    lock.notifyAll();
                }
                lockNum.set(0);
            }
        }
        return (T) super.poll();
    }


    public static void main(String[] args) {
        MyLinkedQueue<String> queue = new MyLinkedQueue<>();
        for(int i=0; i<10; i++){
            queue.put("str" + i);
        }

        for(int i=0; i<10; i++){
            System.out.println(queue.poll());
        }
    }
}

输出

str0
str1
str2
str3
str4
str5
当前队列数量:4解锁线程数量:0
str6
当前队列数量:3解锁线程数量:0
str7
当前队列数量:2解锁线程数量:0
str8
当前队列数量:1解锁线程数量:0
str9
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值