右耳菌-邓小白的Java架构师的修炼之路

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

2022-06-09  本文已影响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

本文总结


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

上一篇 下一篇

猜你喜欢

热点阅读