仿写Mybatis中的Cache
在mybatis中提供了很多Cache,每一个Cache都有不同的特性

其中,我们使用了装饰者模式来仿写Cache
首先定义规范接口
public interface Cache {
public void putObject(Object key,Object value);
public Object getObject(Object key);
public Object removeObject(Object key);
public void clear();
public int size();
}
普通的缓存对象,内部使用的是HashMap作为数据存储,没有提供缓存淘汰机制,容易引发
内存溢出现象,以及线程不安全问题。
/**
* 负责真正存储数据的一个对象
* 将数据存储到map中
*/
public class PerpetualCache implements Cache {
/**
* 内部真正的cache对象
*/
private Map<Object,Object> cache=null;
@Override
public void putObject(Object key, Object value) {
cache=new HashMap<>();
cache.put(key,value);
}
@Override
public Object getObject(Object key) {
if(cache==null) {
return null;
}
return cache.get(key);
}
@Override
public Object removeObject(Object key) {
if(cache==null) {
return null;
}
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
@Override
public int size() {
return cache.size();
}
@Override
public String toString() {
return cache.toString();
}
public static void main(String[] args) {
Cache cache=new PerpetualCache();
cache.putObject("A", 100);
cache.putObject("B", 200);
cache.putObject("C", 300);
System.out.println(cache);
cache.removeObject("D");
cache.clear();
System.out.println(cache.size());
}
}
线程安全的缓存对象,使用的是同步关键字实现
package com.cy.java.Cache;
/**
* 线程安全的cache对象
* synchronized 实现
*/
public class SynchronizedCache implements Cache{
private Cache cache;
public SynchronizedCache(Cache cache) {
this.cache=cache;
}
@Override
public synchronized void putObject(Object key, Object value) {
cache.putObject(key, value);
}
@Override
public synchronized Object getObject(Object key) {
return cache.getObject(key);
}
@Override
public synchronized Object removeObject(Object key) {
return cache.removeObject(key);
}
@Override
public synchronized void clear() {
cache.clear();
}
@Override
public synchronized int size() {
return cache.size();
}
@Override
public String toString() {
return cache.toString();
}
public static void main(String[] args) {
SynchronizedCache cache=
new SynchronizedCache(new PerpetualCache());
cache.putObject("A", 100);
cache.putObject("B", 200);
cache.putObject("C", 300);
System.out.println(cache);
}
}
先进先出缓存,实现FIFO的缓存淘汰机制
package com.cy.java.cache;
/**
* 基于FIFO(先进先出)算法,定义FifoCache,当缓存满的时候,从缓存中移除头部数据
* @author Jason_liu
* 2020年5月14日,上午9:12:13
*/
public class FifoCache implements Cache {
private Cache cache;
/**最大容量*/
private int maxCap;
/**通过此队列记录元素的添加顺序*/
private Deque<Object> keyOrders;
public FifoCache(Cache cache,int maxCap) {
this.cache=cache;
this.maxCap=maxCap;
this.keyOrders=new LinkedList<>();
}
/**向cache中放数据:cache满了要淘汰最早放入的元素*/
@Override
public void putObject(Object key, Object value) {
//记录key的添加顺序
this.keyOrders.addLast(key);
//检查容器是否已满,满了则移除头部元素
if(keyOrders.size()>maxCap) {
//先移除队列中保存的key值,基于key值移除缓存中数据
Object eldestKey=keyOrders.removeFirst();
cache.removeObject(eldestKey);
}
//添加新元素
cache.putObject(key, value);
}
@Override
public Object getObject(Object key) {
return cache.getObject(key);
}
@Override
public Object removeObject(Object key) {
Object object=cache.removeObject(key);
keyOrders.remove(key);
return object;
}
@Override
public void clear() {
cache.clear();
keyOrders.clear();
}
@Override
public int size() {
return cache.size();
}
@Override
public String toString() {
return cache.toString();
}
public static void main(String[] args) {
Cache cache=new FifoCache(new PerpetualCache(), 3);
cache.putObject("A", 100);
cache.putObject("B", 200);
cache.putObject("C", 300);
cache.putObject("D", 400);
System.out.println(cache);//BCD
cache.getObject("B");
cache.putObject("E",500);
System.out.println(cache);//CDE
}
}
基于LRU算法实现的Cache
/**
* LRU缓存的简单实现
* @author Jason_liu
* 2020年5月14日,上午9:34:50
*/
public class LruCache implements Cache {
private Cache cache;
/**保存不常用的Key值*/
private Object eldestKey;
/**主要使用此对象实现LRU机制*/
private Map<Object,Object> keyMap;
public LruCache(Cache cache,int maxCap) {
this.cache=cache;
this.keyMap=new LinkedHashMap<Object, Object>(){
/*每次调用put方法都会自动调用此方法*/
protected boolean removeEldestEntry(
Map.Entry<Object,Object> eldest) {
/*如果cache的长度大于最大的容量
* 则开始获取最不常用的cache的key
* 保存在eldestKey这个属性上面
*/
boolean isFull=size()>maxCap;
if(isFull) {
eldestKey=eldest.getKey();
}
return isFull;
}
};
}
@Override
public void putObject(Object key, Object value) {
cache.putObject(key, value);
keyMap.put(key, value);
if(eldestKey!=null) {
cache.removeObject(eldestKey);
eldestKey=null;
}
}
@Override
public Object getObject(Object key) {
keyMap.get(key);
return cache.getObject(key);
}
@Override
public Object removeObject(Object key) {
return cache.removeObject(key);
}
@Override
public void clear() {
cache.clear();
keyMap.clear();
}
@Override
public int size() {
return cache.size();
}
@Override
public String toString() {
return cache.toString();
}
public static void main(String[] args) {
SynchronizedCache cache=
new SynchronizedCache( new LruCache(new PerpetualCache(),3));
cache.putObject("A", 100);
cache.putObject("B", 100);
cache.putObject("C", 100);
cache.putObject("D", 100);
System.out.println(cache.getObject("A"));
System.out.println(cache);
}
}
软引用Cache,内存不足数据自动被清除
/**
* 软引用cache
* @author Jason_liu
* 2020年5月14日,下午1:57:22
*/
public class SoftCache implements Cache {
private Cache cache;
/**基于此队列记录被垃圾回收的对象:当软引用引用的对象被GC时
* 会将被GC的对象的软引用存储到ReferenceQueue中*/
private ReferenceQueue<Object> garbageQueue;
public SoftCache(Cache cache) {
this.cache=cache;
this.garbageQueue=new ReferenceQueue<Object>();
}
/**
* 构建软引用类型
*/
private static class SoftEntry extends SoftReference<Object>{
private Object key;
public SoftEntry(Object key,Object referent,
ReferenceQueue<? super Object> rQueue) {
super(referent, rQueue);
this.key=key;
}
}
/**从队列中取出已经被gc对象,然后从cache中移除*/
private void removeGarbageObjects() {
SoftEntry softEntry=null;
while((softEntry=(SoftEntry)garbageQueue.poll())!=null) {
cache.removeObject(softEntry.key);
}
}
@Override
public void putObject(Object key, Object value) {
//移除队列中记录的对象
removeGarbageObjects();
//对象进行软引用关联
cache.putObject(key, new SoftEntry(key, value,
garbageQueue));
}
/**
* 基于key获取cache中的SoftReference引用的对象
*/
@Override
public Object getObject(Object key) {
//1.从cache获取引用对象并校验
SoftEntry softEntry=(SoftEntry)cache.getObject(key);
if(softEntry==null)cache.removeObject(key);
//2.基于引用获取引用的对象并校验
Object target=softEntry.get();
if(target==null)cache.removeObject(key);
return target;
}
@Override
public Object removeObject(Object key) {
//1.移除垃圾对象
removeGarbageObjects();
//2.移除目标引用对象
Object target=cache.removeObject(key);
return target;
}
@Override
public void clear() {
removeGarbageObjects();
cache.clear();
}
@Override
public int size() {
removeGarbageObjects();
return cache.size();
}
@Override
public String toString() {
return cache.toString();
}
/**
* 测试:在内存不足和内存充足的情况下cache中的数据变化
* @param args
*/
//-Xmx5m -Xms5m -XX:+PrintGCDetails
public static void main(String[] args) {
Cache cache=new SoftCache(new PerpetualCache());
cache.putObject("A", new byte[1024*1024]);
cache.putObject("B", new byte[1024*1024]);
cache.putObject("C", new byte[1024*1024]);
cache.putObject("D", new byte[1024*1024]);
cache.putObject("E", new byte[1024*1024]);
cache.putObject("F", new byte[1024*1024]);
cache.putObject("K", new byte[1024*1024]);
System.out.println(cache.size());
System.out.println(cache);
}
}
序列化Cache
/**
* 序列化cache
* 1)对象存储到cache之前先进行序列化,让后进行存储,存储cache的对象是一个字节数组的引用。
* 2)从cache获取对象需要将字节数组反序列化。
* @author Jason_liu
* 2020年5月14日,下午2:00:24
*/
public class SerializedCache implements Cache {
private Cache cache;
public SerializedCache(Cache cache) {
this.cache=cache;
}
/**对象序列化*/
private byte[] serialize(Object value) {
//1.构建流对象
ByteArrayOutputStream bos=null;
ObjectOutputStream out=null;
bos=new ByteArrayOutputStream();//内置一个可扩容的数组
try {
out=new ObjectOutputStream(bos);//装饰模式
//2.对象序列化
out.writeObject(value);//obj对象需要实现Serializable
out.flush();
return bos.toByteArray();
}catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("序列化失败");
}finally {
//3.释放资源
try{if(out!=null) {out.close();out=null;}}catch(Exception e) {}
}
}
/**对象的反序列化*/
private Object deserialize(byte[] array){
//1.创建流对象
ByteArrayInputStream bis=null;
ObjectInputStream ois=null;
bis=new ByteArrayInputStream(array);
try {
ois=new ObjectInputStream(bis);
//2.对象的反序列化
Object obj=ois.readObject();
return obj;
}catch(Exception e) {
e.printStackTrace();
throw new RuntimeException("反序列化失败");
}finally {
//3.释放资源
try{if(bis!=null) {bis.close();bis=null;}}catch(Exception e) {}
}
}
@Override
public void putObject(Object key, Object value) {
//1.将对象序列化
byte[] array=serialize(value);
//2.将序列化后的字节数组引用存储到cache
cache.putObject(key,array);
}
@Override
public Object getObject(Object key) {
//1.基于key获取缓存中的字节数组引用
byte[] array=(byte[])cache.getObject(key);
//2.将字节数组反序列化为对象
return deserialize(array);
}
@Override
public Object removeObject(Object key) {
return deserialize((byte[])cache.removeObject(key));
}
@Override
public void clear() {
cache.clear();
}
@Override
public int size() {
return cache.size();
}
public static void main(String[] args) {
Cache cache=new SerializedCache(new PerpetualCache());
cache.putObject("A", 500);
Object a1=cache.getObject("A");
Object a2=cache.getObject("A");
System.out.println(a1==a2);//false
}
}

本文深入探讨并仿写了Mybatis中的多种Cache机制,包括基于装饰者模式的普通缓存、线程安全缓存、FIFO缓存、LRU缓存、软引用缓存和序列化缓存,详细解析了每种缓存的特性和实现方式。

被折叠的 条评论
为什么被折叠?



