自定义缓存算法

🍎 定义 Cache 接口

定义缓存接口的规范:

package cache;

/**
 * @author: yueLQ
 * @date: 2022-10-01 17:04
 * <p>
 * 自定义缓存接口
 */
public interface Cache {

    /**
     * 获取存储的数据
     *
     * @param key
     * @param val
     */
    public void putObj(Object key, Object val);

    /**
     * 获取存储的数据
     *
     * @param key
     * @return
     */
    public Object getObj(Object key);

    /**
     * 移除缓存中的数据
     *
     * @return
     */
    public Object removeObj(Object key);

    /**
     * 清除缓存中的数据
     */
    public void clear();

    /**
     * 返回缓存中数据的个数
     *
     * @return
     */
    public int size();
}


🍎 永久缓存 PerpetualCache

自定义永久 cache ,不定义任何缓存策略。

public class PerpetualCache implements Cache {

    /**
     * JDK 中的 hashMap 存在线程不安全的问题
     * 1. JDK7 之前可能出现死循环(环形链)
     * 2. JDK8 之后会出现值覆盖的情况
     */
    Map<Object, Object> cache = new HashMap<>();

    /**
     * 获取存储的数据
     *
     * @param key
     * @param val
     */
    @Override
    public void putObj(Object key, Object val) {
        cache.put(key, val);
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object getObj(Object key) {
        return cache.get(key);
    }

    /**
     * 移除缓存中的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object removeObj(Object key) {
        return cache.remove(key);
    }

    /**
     * 清除缓存中的数据
     */
    @Override
    public void clear() {
        cache.clear();
    }

    /**
     * 返回缓存中数据的个数
     *
     * @return
     */
    @Override
    public int size() {
        return cache.size();
    }
}

问题:

  1. 可能出现内存溢出的情况,不可存储大量数据。
  2. 可能存在线程安全问题,不可进行多线程的数据共享。

🍎 FifoCache 缓存

基于 Fifo 算法,对象满了按照先进先出的原则移除对象,实现 cache对象。


public class FifoCache implements Cache {

    /**
     * 借助此对象进行数据的缓存
     */
    private Cache cache;

    /**
     * 借助此队列,记录 key 的顺序
     */
    private Deque<Object> keyOrders;

    /**
     * 通过此变量记录 cache 可以存储对象的个数
     */
    private int maxCap;

    /**
     * 构造器
     *
     * @param cache
     * @param maxCap
     */
    public FifoCache(Cache cache, int maxCap) {
        this.cache = cache;
        this.keyOrders = new LinkedList<>();
        this.maxCap = maxCap;
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @param val
     */
    @Override
    public void putObj(Object key, Object val) {
        // 1. 记录 key 的顺序,其实存储 key 添加到队列最后位置
        keyOrders.add(key);
        // 2. 检测 cache 中存储的数据是否满了,如果满了的话则要清除
        if (keyOrders.size() > maxCap) {
            // 移除掉第一个 key
            Object eldesKey = keyOrders.removeFirst();
            cache.removeObj(eldesKey);
        }
        // 3. 放入新的对象
        cache.putObj(key, val);
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object getObj(Object key) {
        return cache.getObj(key);
    }

    /**
     * 移除缓存中的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object removeObj(Object key) {
        Object obj = cache.removeObj(key);
        keyOrders.remove(key);
        return obj;
    }

    /**
     * 清除缓存中的数据
     */
    @Override
    public void clear() {
        cache.clear();
        keyOrders.clear();
    }

    @Override
    public String toString() {
        return cache.toString();
    }

    /**
     * 返回缓存中数据的个数
     *
     * @return
     */
    @Override
    public int size() {
        return cache.size();
    }

    public static void main(String[] args) {
        FifoCache cache = new FifoCache(new PerpetualCache(), 3);
        cache.putObj("A",100);
        cache.putObj("B",200);
        cache.putObj("C",300);
        cache.getObj("A");
        cache.putObj("D",400);
        cache.putObj("E",500);
        System.out.println(cache);
    }
}


🍎 LRU 算法缓存

基于 LRU 算法实现缓存,在缓存满的时候,淘汰最近最少访问的对象。
LRU算法:最近做少使用。

public class LruCache implements Cache {

    /**
     * 负责存储数据的目标 cache
     */
    private Cache cache;

    /**
     * 通过该属性记录要移除的数据对象,最近最少访问
     */
    private Object eldesKey;

    /**
     * 通过此 map 记录 key 访问顺序,次数
     */
    private LinkedHashMap<Object, Object> keyMap;

    /**
     * @param cache
     * @param cap   代表最大的容量
     */
    public LruCache(Cache cache, int cap) {
        this.cache = cache;
        this.keyMap = new LinkedHashMap<Object, Object>(cap, 0.75f, true) {
            /**
             *  该方法在执行 put 时候会执行
             *  该方法删除最旧的条目
             *  如果打算从 map 中删除最旧的条目,则 map 返回 true
             *  如果不打算从 map 中删除或者保留该条目,则返回 false
             * @param eldest
             * @return
             */
            @Override
            protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
                Boolean ifFull = size() > cap;
                if (ifFull) {
                    eldesKey = eldest.getKey();
                }
                return ifFull;
            }
        };
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @param val
     */
    @Override
    public void putObj(Object key, Object val) {
        // 存储对象
        cache.putObj(key, val);
        // 记录 key 的访问顺序
        keyMap.put(key, val);
        // 移除最近最少访问的元素
        if (eldesKey != null) {
            cache.removeObj(eldesKey);
            eldesKey = null;
        }

    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object getObj(Object key) {
        // 记录 key 的访问数据,刷新使用数据
        keyMap.get(key);
        // 返回缓存数据
        return cache.getObj(key);
    }

    /**
     * 移除缓存中的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object removeObj(Object key) {
        return cache.removeObj(key);
    }

    /**
     * 清除缓存中的数据
     */
    @Override
    public void clear() {
        cache.clear();
    }

    /**
     * 返回缓存中数据的个数
     *
     * @return
     */
    @Override
    public int size() {
        return cache.size();
    }

    @Override
    public String toString() {
        return cache.toString();
    }


    public static void main(String[] args) {
        Cache cache = new LruCache(new PerpetualCache(), 3);
        cache.putObj("A", 100);
        cache.putObj("B", 200);
        cache.putObj("C", 300);
        cache.getObj("A");
//        cache.getObj("C");
        cache.putObj("D", 400);
        cache.putObj("E", 500);
        System.out.println(cache);
    }
}


🍎 序列化的 cache

序列化的 cache

  1. 对象存储到 cache 之前,先进行序列化,存储到 cache 对象是一个字节的引用
  2. 反序列化
package cache;

import java.io.*;

/**
 * @author: yueLQ
 * @date: 2022-10-01 21:51
 * 
 */
public class SerializedCache implements Cache {

    private Cache cache;

    public SerializedCache(Cache cache) {
        this.cache = cache;
    }

    // 基础加强:对象序列化
    private byte[] serializa(Object val) {
        // 1. 构建对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream outputStream = null;
        bos = new ByteArrayOutputStream();
        try {
            // 装饰模式
            outputStream = new ObjectOutputStream(bos);
            // 2. 对象序列化
            outputStream.writeObject(val);
            outputStream.flush();
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("对象序列化失败!");
        } finally {
            if (bos != null)

                try {
                    bos.close();
                    bos = null;
                } catch (Exception e) {
                }
            if (outputStream != null)

                try {
                    outputStream.close();
                    outputStream = null;
                } catch (Exception e2) {
                }
        }
    }

    private Object deserialize(byte[] array) {
        // 创建流对象
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //1.1构建字节数组输入流,此对象可以直接读取数组中的字节信息
            bis = new ByteArrayInputStream(array);
            //1.2构建对象输入流(对象反序列化)
            ois = new ObjectInputStream(bis);
            //2.反序列化对象
            Object obj = ois.readObject();
            return obj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //3.关闭流对象
            if (bis != null)

                try {
                    bis.close();
                    bis = null;
                } catch (Exception e) {
                }
            if (ois != null)

                try {
                    ois.close();
                    ois = null;
                } catch (Exception e2) {
                }
        }
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @param val
     */
    @Override
    public void putObj(Object key, Object val) {
        // 1.将对象序列化
        byte[] array = serializa(val);
        //2.将序列化的字节存储带cache
        cache.putObj(key, array);
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object getObj(Object key) {
        // 1.基于key获取缓存中的字节数组的引用
        //2.将字节数组反序列化为对象
        return deserialize((byte[]) cache.getObj(key));
    }

    /**
     * 移除缓存中的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object removeObj(Object key) {
        return cache.removeObj(key);
    }

    /**
     * 清除缓存中的数据
     */
    @Override
    public void clear() {
        cache.clear();
    }

    /**
     * 返回缓存中数据的个数
     *
     * @return
     */
    @Override
    public int size() {
        return cache.size();
    }

    public static void main(String[] args) {
        Cache cache = new SerializedCache(new PerpetualCache());
        cache.putObj("A", 200);
        cache.putObj("B", 300);
        Object v1 = cache.getObj("A");
        Object v2 = cache.getObj("A");
        System.out.println(v1 == v2);
        System.out.println(v1);
        System.out.println(v2);
    }
}


🍎 线程安全的 cache

基于 synchronized 关键字构建线程安全方法。

public class SynchronizedCache implements Cache {

    private Cache cache;

    public SynchronizedCache(Cache cache) {
        this.cache = cache;
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @param val
     */
    @Override
    public synchronized void putObj(Object key, Object val) {
        cache.putObj(key,val);
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @return
     */
    @Override
    public synchronized Object getObj(Object key) {
        return cache.getObj(key);
    }

    /**
     * 移除缓存中的数据
     *
     * @param key
     * @return
     */
    @Override
    public synchronized Object removeObj(Object key) {
        return cache.removeObj(key);
    }

    /**
     * 清除缓存中的数据
     */
    @Override
    public synchronized void clear() {
        cache.clear();
    }

    /**
     * 返回缓存中数据的个数
     *
     * @return
     */
    @Override
    public int size() {
        return 0;
    }
}

优势: 在多线程并发环境下可以保证 map 中数据的正确性。
劣势: 性能和并发都会降低。


软引用

package cache;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

/**
 * @author: yueLQ
 * @date: 2022-10-02 7:39
 * <p>
 * 软引用
 */
public class SoftCache implements Cache {

    private Cache cache;

    /**
     * 基于此对象记录被垃圾回收的对象,当软引用使用的对象被 GC 时
     * 会将被 GC 的对象软引用存储到这个队列中
     */
    private ReferenceQueue<Object> queue = new ReferenceQueue<>();

    public SoftCache(Cache cache) {
        this.cache = cache;
        this.queue = new ReferenceQueue<>();
    }

    /**
     * 软引用
     */
    class SoftEntity extends WeakReference<Object> {

        private Object key;

        /**
         * @param key
         * @param referent 被弱引用的对象,此对象 cache 的 value
         * @param queue    引用队列
         */
        public SoftEntity(Object key, Object referent, ReferenceQueue<? super Object> queue) {
            super(referent, queue);
            this.key = key;
        }


    }

    // 从队列中取出 GC 对象,然后从 cache 中移除
    private void removeGarbageObject() {
        SoftEntity soft = null;
        while ((soft = (SoftEntity) queue.poll()) != null) {
            cache.removeObj(soft.key);
        }
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @param val
     */
    @Override
    public void putObj(Object key, Object val) {
        // 1. 移除队列中记录对象
        // 2. 对象进行软引用然后存储到 cache 中
        cache.putObj(key, new SoftEntity(key, val, queue));
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object getObj(Object key) {
        // 从 cache 中获取引用对象并且校验
        SoftEntity soft = (SoftEntity) cache.getObj(key);
        if (soft == null) {
            cache.removeObj(key);
        }
        // 从引用中获取引用对象
        Object target = soft.get();
        if (target == null) {
            cache.removeObj(key);
        }
        return target;
    }

    /**
     * 移除缓存中的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object removeObj(Object key) {
        // 移除垃圾对象
        removeGarbageObject();
        // 移除目标引用对象
        return cache.removeObj(key);
    }

    /**
     * 清除缓存中的数据
     */
    @Override
    public void clear() {
        removeGarbageObject();
        cache.clear();
    }

    /**
     * 返回缓存中数据的个数
     *
     * @return
     */
    @Override
    public int size() {
        removeGarbageObject();
        return cache.size();
    }
}


🍎 为目标 cache 添加日志

public class LoggingCache implements Cache {

    private Cache cache;

    // 请求次数
    private int request;

    // 命中次数(表示从缓存中取到数据)
    private int hits;


    public LoggingCache(Cache cache) {
        this.cache = cache;
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @param val
     */
    @Override
    public void putObj(Object key, Object val) {
        cache.putObj(key, val);
    }

    /**
     * 获取存储的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object getObj(Object key) {
//        Auto-generated method stub
        request++;
        Object obj = cache.getObj(key);
        if (obj != null) hits++;
        return obj;
    }

    /**
     * 移除缓存中的数据
     *
     * @param key
     * @return
     */
    @Override
    public Object removeObj(Object key) {
        return cache.removeObj(key);
    }

    /**
     * 清除缓存中的数据
     */
    @Override
    public void clear() {
        cache.clear();
    }

    /**
     * 返回缓存中数据的个数
     *
     * @return
     */
    @Override
    public int size() {
        return cache.size();
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光头小小强007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值