一步步,利用ConcurrentHashMap实现缓存工具类

很多时候都需要使用到缓存工具-(不过目前有很多专业的缓存工具,写这个东西仅仅是为了梳理缓存的逻辑)

首先定义一个存储工具使用的 ConcurrentHashMap(支持并发)

private volatile Map data = new ConcurrentHashMap<>(16);

缓存应该具备到期自动删除的功能,所以需要对要存储的对象进行包装 

@Data
    class CacheObject {
        /**
         * 需要存储的值
         */
        private Object value;
        /**
         * dateline(存储的时候计算)
         */
        private long expire;

        public CacheObject() {
        }

        public boolean isExpire() {
            return System.currentTimeMillis() > expire;
        }

        public CacheObject(Object value, long expire) {
            this.value = value;
            this.expire = expire;
        }
    }

增加添加和获取的方法

    在添加数据是要考虑过期时间

/**
     * @param key    要存储的key
     * @param value  要存储的值
     * @param expire 过期时间
     * @return
     */
    public boolean add(String key, Object value, long expire) {
        CacheObject cacheObject = new CacheObject(value, expire + System.currentTimeMillis());
        data.put(key, cacheObject);
        return true;
    }

    /**
     * 根据key获取值
     *
     * @param key 指定的key
     * @return
     */
    public Object get(String key) {
        CacheObject cacheObject = data.get(key);
        if (Objects.isNull(cacheObject)) {
            return null;
        }
        return cacheObject.getValue();
    }

 此时还需要一个定时任务要不停的去监听数据是否过期

  /**
     * 定时任务删除过期的数据
     */
    private void init() {
        ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(5);

        scheduled.scheduleWithFixedDelay(() -> {
            data.entrySet().removeIf(entry -> Objects.nonNull(entry) && entry.getValue().isExpire());
        }, 100, 100, TimeUnit.MILLISECONDS);
    }

 测试代码

  public static void main(String[] args) throws InterruptedException {
        HashMapCache cache = new HashMapCache();
        cache.add("AAA", "AAA", 2000);
        cache.add("BBB", "BBB", 2000);
        cache.add("CCC", "CCC", 2000);
        cache.add("DDD", "DDD", 2000);
        System.out.println(cache.get("BBB"));
        Thread.sleep(5001);
        System.out.println(cache.get("BBB"));
    }

 考虑垃圾回收机制问题,所以升级为软引用,在内存爆掉前释放资源,全部代码如下

package com.milla.study.netbase.expert.memory;


import lombok.Data;

import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author milla
 */
public class HashMapCacheSoftReference {
    private volatile Map> data = new ConcurrentHashMap<>(16);

    public static void main(String[] args) throws InterruptedException {
        HashMapCacheSoftReference cache = new HashMapCacheSoftReference();
        System.out.println(cache.get("BBB"));
        Thread.sleep(5001);
        System.out.println(cache.get("BBB"));
    }

    public HashMapCacheSoftReference() {
        init();
    }

    private void init() {
        ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(5);

        scheduled.scheduleWithFixedDelay(() -> {
            data.entrySet().removeIf(o -> Optional.ofNullable(o.getValue()).map(SoftReference::get).map(CacheObject::isExpire).orElse(false));
        }, 100, 100, TimeUnit.MILLISECONDS);
    }


    public boolean add(String key, Object value, long expire) {
        if (key == null) {
            return false;
        }
        if (Objects.isNull(value)) {
            return false;
        }
        CacheObject cacheObject = new CacheObject(value, expire + System.currentTimeMillis());
        data.put(key, new SoftReference<>(cacheObject));
        return true;
    }

    public Object get(String key) {
        SoftReference cacheObjectSoftReference = data.get(key);
        if (Objects.isNull(cacheObjectSoftReference)) {
            return null;
        }
        CacheObject cacheObject = cacheObjectSoftReference.get();
        if (Objects.isNull(cacheObject)) {
            return null;
        }
        return cacheObject.getValue();
    }


    @Data
    class CacheObject {
        /**
         * 需要存储的值
         */
        private Object value;
        /**
         * dateline(存储的时候计算)
         */
        private long expire;

        public CacheObject() {
        }

        public boolean isExpire() {
            return System.currentTimeMillis() > expire;
        }

        public CacheObject(Object value, long expire) {
            this.value = value;
            this.expire = expire;
        }
    }
}

 ps: 有很多优秀的缓存工具,考虑的更加细致,所以只需要知道大体实现思路即可,没必要自己造轮子

以下是guava缓存的部分 

maven依赖 

       
        
            com.google.guava
            guava
            28.2-jre
        

 测试代码

 public static void main(String[] args) throws ExecutionException {
        CacheBuilder build = CacheBuilder.newBuilder();
        //并发级别
        LoadingCache cache = build.concurrencyLevel(8)
                //设置过期时间
                .expireAfterWrite(8, TimeUnit.SECONDS)
                //写缓存之后刷新
                .refreshAfterWrite(1, TimeUnit.SECONDS)
                //初始化容量
                .initialCapacity(10)
                //删除监听事件
                .removalListener(notification -> System.out.println("我已经被删除了"))
                //统计命中率
                .recordStats()
                //最大容量是100
                .maximumSize(100)
                //如果没有数据进行数据的获取等
                .build(new CacheLoader() {
                    @Override
                    public Object load(Object key) throws Exception {
                        System.out.println("缓存没有,从数据库中加载:" + key);
                        return UUID.randomUUID().toString();
                    }
                });

        Object o = cache.get("1111");
        System.out.println(o);
        System.out.println(cache.get("1111"));
        //命中率
        System.out.println(cache.stats().toString());
    }

 

你可能感兴趣的:(高性能编程)