文章目录
🍎 定义 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();
}
}
问题:
- 可能出现内存溢出的情况,不可存储大量数据。
- 可能存在线程安全问题,不可进行多线程的数据共享。
🍎 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
- 对象存储到 cache 之前,先进行序列化,存储到 cache 对象是一个字节的引用
- 反序列化
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();
}
}