美文网首页右耳菌-邓小白的Java架构师的修炼之路
Java 缓存优化2 - 实现简单的内存缓存

Java 缓存优化2 - 实现简单的内存缓存

作者: 右耳菌 | 来源:发表于2022-06-09 13:43 被阅读0次

    不使用淘汰策略的map的缓存实现方式

    • 工具类
    package cn.lazyfennec.mapcache;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Author: Neco
     * @Description: 基于map的缓存工具类
     * @Date: create in 2022/6/8 21:55
     */
    
    public class CacheProvider {
    
        // 存放缓存的集合
        private final static Map<String, CacheData> caches = new ConcurrentHashMap<>();
        // 定时器线程池,用于清除过期缓存
        private final static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
    
        // 1. 获取
        public synchronized <T> T get(String key) {
            CacheData cacheData = caches.get(key);
            return cacheData == null ? null : (T) cacheData.data;
        }
        // 2. 插入
        // 插入数据,不设置过期时间
        public synchronized <T> void put(String key, T data) {
            put(key, data, 0L); // 0L表示不设置过期时间, 小于等于0都表示不设置过期时间
        }
        // 插入数据,设置过期时间
        public synchronized <T> void put(String key, T data, Long expire) {
            if(expire > 0) {
                executor.schedule(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (this) {
                            caches.remove(key);
                        }
                    }
                }, expire, TimeUnit.MILLISECONDS); // 设置指定的时间后执行删除
                caches.put(key, new CacheData(data, expire));
            } else {
                caches.put(key, new CacheData(data, 0L));
            }
        }
        // 3. 删除
        public synchronized <T> T remove(String key) {
            CacheData cacheData = caches.remove(key);
            return cacheData == null ? null : (T) cacheData.data;
        }
        // 4. 总数
        public synchronized Integer size() {
            return caches.size();
        }
    
        public class CacheData {
            // 数据
            private Object data;
            // 失效时间
            private Long expire;
    
            public CacheData(Object data, Long expire) {
                this.data = data;
                this.expire = expire;
            }
        }
    }
    
    • 测试类
    package cn.lazyfennec.mapcache;
    
    import org.junit.Test;
    
    /**
     * @Author: Neco
     * @Description: 缓存测试类
     * @Date: create in 2022/6/8 22:22
     */
    public class CacheTest {
        @Test
        public void test() throws InterruptedException {
            CacheProvider cache = new CacheProvider();
    
            String key = "id";
    
            // 不设置过期时间
            System.out.println("*********** 不设置过期时间 ***********");
            cache.put(key, 123);
            System.out.println("key:" + key + ", value:" + cache.get(key));
            System.out.println("key:" + key + ", value:" + cache.remove(key));
            System.out.println("key:" + key + ", value:" + cache.get(key));
    
            // 设置过期时间
            System.out.println("*********** 设置过期时间 ***********");
            cache.put(key, 123456, 1000L);
            System.out.println("key:" + key + ", value:" + cache.get(key));
    
            Thread.sleep(2000);
            System.out.println("key:" + key + ", value:" + cache.get(key));
        }
    }
    
    • 运行结果
    *********** 不设置过期时间 ***********
    key:id, value:123
    key:id, value:123
    key:id, value:null
    *********** 设置过期时间 ***********
    key:id, value:123456
    key:id, value:null
    

    使用FIFO淘汰策略的map的缓存实现方式

    • 工具类
    package cn.lazyfennec.mapcache;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Author: Neco
     * @Description: 基于map的缓存工具类, 使用FIFO缓存策略
     * @Date: create in 2022/6/8 21:55
     */
    
    public class FIFOCacheProvider {
    
        // 存放缓存的集合
        private Map<String, CacheData> caches = null;
        // 定时器线程池,用于清除过期缓存
        private final static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
    
        // FIFO
        private static int MAX_CACHE_SIZE = 0; // 最大的容量
        private final float LOAD_FACTORY = 0.75f; // 填充因子
    
        public FIFOCacheProvider(int maxCacheSize) {
            MAX_CACHE_SIZE = maxCacheSize;
            // 根据缓存大小 和 填充因子计算cache的容量
            int capacity = (int) (Math.ceil(MAX_CACHE_SIZE / LOAD_FACTORY) + 1);
            caches = new LinkedHashMap<String, CacheData>(capacity, LOAD_FACTORY, false) {
                // 是否刪除最旧数据的判定方法
                @Override
                protected boolean removeEldestEntry(Map.Entry<String, CacheData> eldest) {
                    return size() > MAX_CACHE_SIZE;
                }
            };
        }
    
        // 1. 获取
        public synchronized <T> T get(String key) {
            CacheData cacheData = caches.get(key);
            return cacheData == null ? null : (T) cacheData.data;
        }
    
        // 2. 插入
        // 插入数据,不设置过期时间
        public synchronized <T> void put(String key, T data) {
            put(key, data, 0L); // 0L表示不设置过期时间, 小于等于0都表示不设置过期时间
        }
    
        // 插入数据,设置过期时间
        public synchronized <T> void put(String key, T data, Long expire) {
            if (expire > 0) {
                executor.schedule(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (this) {
                            caches.remove(key);
                        }
                    }
                }, expire, TimeUnit.MILLISECONDS); // 设置指定的时间后执行删除
                caches.put(key, new CacheData(data, expire));
            } else {
                caches.put(key, new CacheData(data, 0L));
            }
        }
    
        // 3. 删除
        public synchronized <T> T remove(String key) {
            CacheData cacheData = caches.remove(key);
            return cacheData == null ? null : (T) cacheData.data;
        }
    
        // 4. 总数
        public synchronized Integer size() {
            return caches.size();
        }
    
        public class CacheData {
            // 数据
            private Object data;
            // 失效时间
            private Long expire;
    
            public CacheData(Object data, Long expire) {
                this.data = data;
                this.expire = expire;
            }
        }
    
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, CacheData> entry: caches.entrySet()) {
                sb.append(entry.getKey()).append("=").append(entry.getValue().data).append("\n");
            }
            return sb.toString();
        }
    }
    
    • 测试类
    package cn.lazyfennec.mapcache;
    
    import org.junit.Test;
    
    import java.util.UUID;
    
    /**
     * @Author: Neco
     * @Description: 缓存测试类, FIFO
     * @Date: create in 2022/6/8 22:22
     */
    public class FIFOCacheTest {
    
        @Test
        public void test() {
            FIFOCacheProvider cache = new FIFOCacheProvider(10);
            for (int i = 0; i < 15; i++) {
                cache.put("id" + i, UUID.randomUUID());
            }
            System.out.println("缓存的大小:" + cache.size());
            System.out.println("缓存数据:\n" + cache.toString());
        }
    
    }
    
    • 运行结果
    缓存的大小:10
    缓存数据:
    id5=161f7873-d053-495f-a6ea-c79642f38ed3
    id6=f03b0056-ccd2-4b70-9b2a-ab61056418dc
    id7=a1164a62-87fd-40e2-a49a-33339f8c5cf1
    id8=c0d2113a-1c0f-4003-a0c7-791114f5d9f9
    id9=cdeddb2c-290b-49dc-b6c9-545cc9e66754
    id10=be45b62d-ed81-4401-988a-e75cd53689ed
    id11=716e1262-e44c-4e4c-a334-d6e5db8bad48
    id12=0f2ae9b5-1c07-47e0-8a30-16ea3d24ae22
    id13=188db819-d23e-4a5c-a071-65269369170c
    id14=1ceb604c-8eab-489f-a0a1-0181a5f5831c
    

    本文总结

    • 实现并发读写?基于ConcrrentHashMap
    • 实现缓存的定时、定期清除? 基于定时器
    • 实现缓存的FIFO淘汰? 基于队列

    如果觉得有收获就点个赞吧,更多知识,请点击关注查看我的主页信息哦~

    相关文章

      网友评论

        本文标题:Java 缓存优化2 - 实现简单的内存缓存

        本文链接:https://www.haomeiwen.com/subject/ujoumrtx.html