自己实现一个内存缓存

         最近了解了下缓存技术,主要分为内存缓存 和磁盘缓存,然后分布式里面有一些消息的推送,节点的一些处理。现在很多的用OSCache,EhCache 等等,资料很多,暂时就不多说了,我这里仅仅为了了解缓存框架的的知识,自己临时写一个缓存,后面可以实现AOP 和spring 结合。

 

 

实现功能:
1.数据都缓存到内存中
2.实现对每个元素Element的缓存时间进行控制
3.对每个缓存Cache 进行创建,以及销毁的管理
4.能对缓存存放个数进行管理
5.通过线程定时检查元素是否过期
6.可以定义缓存清除策略

 

实现思路:
1.Store 接口:定义常用的存取元素get,put的接口
2.AbstractStore:对接口基本方法进行实现
3.MemoryCache:对具体缓存的存取方法进行实现,并执行一定的逻辑控制
4.Cache:增加对缓存整体进行监听,销毁等操作
5.CacheManager:缓存管理器,负责管理缓存的容器

6.CacheConfiguration :一些缓存的配置属性
7.CheckManager:一些检查缓存内容的策略方法
8.EvictionType:枚举清空策略
9.CacheListener:缓存的监听,每次创建缓存就启动,然后定时检查缓存元素是否过期
10.Element: 元素的单元

 

package com.cache;

import java.util.Collection;


public interface Store {
	// 获得缓存名字
	public String getName();
	// 存放元素
	public Element put(Element e);
	public Collection putAll(Collection elements);
	// 获取元素
	public Element get(Object key);
	// 清除元素
	public void clear();
	// 移除元素
	public void remove(Object key);
	public void removeAll(Object[] keys);
	// 获得的元素长度
	public Integer size();
}

 

package com.cache;

import java.util.Map;

/**
 * 抽象类实现
 */
public abstract class AbstractStore implements Store{
	
	protected  Map map;
	
	public AbstractStore(){}
	public AbstractStore(Map map){
		this.map = map;
	}
	
	@Override
	public Element get(Object key) {
		Element e = map.get(key);
		return e;
	}
	
	public Map getAll(){
		return map;
	}
	
	@Override
	public void clear() {
		 map.clear();
	}
	
	@Override
	public Element put(Element e) {
		return map.put(e.getKey(), e);
	}
	
	@Override
	public void remove(Object key) {
		map.remove(key);
	}

	@Override
	public Integer size() {
		return map.size();
	}
	
	@Override
	public void removeAll(Object[] keys) {
		for(int i =0;i

 

package com.cache;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * 缓存的基本方法
 */
public class MemoryCache extends AbstractStore implements Store{
	
	private final CacheConfiguration configure;
	private final static Map map = new HashMap();
	private final CheckManager checkManager;
	
	
	// 暂时仅弄一个构造
	public MemoryCache(CacheConfiguration configure){
		super(map);
		this.configure = configure;
		this.checkManager = new CheckManager(configure, map);
	}
	
	@Override
	public String getName() {
		return configure.getCacheName();
	}

	@Override
	public  Collection  putAll(Collection elements) {
		if(elements == null){
			throw new NullPointerException("elements can't be null");
		}
		check(elements.size());
		for(Element e : elements){
			putElementStatus(e);
			super.put(e);
		}
		return elements;
	}
	
	@Override
	public synchronized Element put(Element e) {
		check(1);
		putElementStatus(e);
		return super.put(e);
	}
	
	
	// 使用一次之后刷新使用过期时间,以及使用次数
	// 并检查该元素是否过期
	public void changeElement(Element e){
		e.addHitCount();
		if(!configure.getEternal()){
			e.refreshLastAccessTime();
		}
	}
	
	// 如果eternal 为true,表示元素永不过期,默认忽略最小元素控制
	public void putElementStatus(Element e){
		if(!configure.getEternal() && ! e.getIsOpen()){
			e.setTimeToIdle(configure.getTimeToIdleSeconds());
			e.setTimeToLive(configure.getTimeToLiveSeconds());
		}else{
			e.setTimeToIdle(0);
			e.setTimeToLive(0);
		}
	}
	
	
	@Override
	public  Element get(Object key) {
		Element e = super.get(key);
		if(e != null){
			if(!e.isExpired()){
				changeElement(e);
			}else{
				synchronized (this) {
					remove(e.getKey());
					e = null;
				}
			}
		}
		return e;
	}
	
	
	
	// 检查元素 是否为空
	public boolean checkElement(Element e){
		if(e == null){
			throw new NullPointerException("Element can't be  null ");
		}
		if(e.getKey() == null){
			throw new NullPointerException("Element key can't be  null ");
		}
		return true;
	}

	@Override
	public synchronized void removeAll(Object[] keys) {
		super.removeAll(keys);
	}
	
	// 检查元素是否超过了
	public void check(int checkSize){
		if(checkSize <= 0){
			return;
		}
		Object[] keys = checkManager.checkConfigure(checkSize);
		if(keys !=null) {
			removeAll(keys);
		}
	}
}

 

 

package com.cache;

/**
 * 这是对缓存 级的控制判断
 */
public class Cache extends MemoryCache{
	
	private CacheConfiguration configure;
	private   CacheListener listener;
	
	public Cache(CacheConfiguration configure) {
		super(configure);
		this.configure = configure;
		if(!configure.getEternal()  &&  configure.getIsNeedCacheCheckListener()){
			listener = new CacheListener(this);
			listener.start();
		}
	}
	public CacheConfiguration getConfigure() {
		return configure;
	}
	
	// 销毁
	public void destory(){
		try{
			super.clear();
			if(listener != null){
				listener.interrupt();
				listener.stop();
				listener = null;
			}
		}catch (Exception e) {
		}
		
	}
}

 

 

 

package com.cache;

import java.util.HashMap;
import java.util.Map;

/**
 * 缓存管理类
 */
public class CacheManager {
	// 默认单例
	private CacheManager(){}
	private static class  Singleton{
		private static CacheManager instance = new CacheManager();
	}
	public static CacheManager getInstance() {
		return Singleton.instance;
	}
	
	public static Map MAP_NAMES_CACHE = new HashMap();
	
	// 存放 取出 缓存对象
	public Cache getCache(String CacheName){
		Cache cache = MAP_NAMES_CACHE.get(CacheName);
		return cache;
	}
	
	public void putCache(Cache cache){
		if(cache != null  && !MAP_NAMES_CACHE.containsKey(cache.getName())){
			MAP_NAMES_CACHE.put(cache.getName(), cache);
		}
	}
	
	// 移除
	public void remove(String cacheName){
		Cache c = MAP_NAMES_CACHE.remove(cacheName);
		c.destory();
	}
	
	// 关闭所有缓存
	public void shutDown(){
		removeAllCaches();
		MAP_NAMES_CACHE.clear();
	}
	
	// 移除所有
	public void removeAllCaches(){
		String [] cacheNames = getCacheNames();
		for(String cacheName : cacheNames){
			remove(cacheName);
		}
	}
	
	// 获得名字
	public String[] getCacheNames(){
        return MAP_NAMES_CACHE.keySet().toArray(new String[0]);
    }
	
}

 

package com.cache;
/**
 * 
 * 这些属性来自于ehcache 
 * 属于缓存级别的 一些控制
 */
public class CacheConfiguration {
	// 缓存的名字
	private String cacheName;
	// 是否需要缓存 循环检查
	private Boolean isNeedCacheCheckListener = false;
	public Boolean getIsNeedCacheCheckListener() {
		return isNeedCacheCheckListener;
	}
	public void setIsNeedCacheCheckListener(Boolean isNeedCacheCheckListener) {
		this.isNeedCacheCheckListener = isNeedCacheCheckListener;
	}
	// 内存最大缓存对象数
	private Integer maxElementsInMemory;
	
	// 缓存元素是否永久有效,一旦设置true ,失效时间 将不起作用,默认false
	private Boolean eternal = false;
	
	// 设置缓存在失效前的允许闲置时间。仅当缓存不是永久有效时使用(timeToLiveSeconds != 0)
	// 可选属性,默认值是0,也就是可闲置时间无穷大。
	private Integer timeToIdleSeconds = 0;
	
	// 设置缓存长允许存活时间,最大时间介于创建时间和失效时间之间.
	// 仅当缓存不是永久有效时使用,默认值是0,也就是缓存存活时间无穷大。
	private Integer timeToLiveSeconds = 0;
	// 对象检测线程运行的时间间隔。表示对象状态的线程多长时间运行一次
	// 这里暂时用来对内存对象的检查
	private Integer diskExpiryThreadIntervalSeconds = 120;
	
	// 如果缓存满了,执行清空策略
	// 可选FIFO,LFU 这里要用枚举类型
	// FIFO :先进先出
	// LFU:最少使用,一直以来最少被使用的,缓存缓存有一个hit属性,清除hit最小的
	// LRU:最近最少使用,缓存元素有个时间戳,当缓存容量满了,而又需要腾出新地方
	// 来缓存的时候,那么现有的缓存缓存中时间戳离当前时间最远的缓存将被清除缓存
	private String memoryStoreEvictionPolicy = EvictionType.LRU.name();
	
	
	// 暂时未用
	// 当缓存数量达到最大值时,允许将缓存写入到磁盘
	private Boolean overflowToDisk = false;
	// 磁盘中最大的缓存对象数,若是0表示无穷大
	private Integer maxElementsOnDisk = 0;
	// 是否在磁盘上持久化,默认false
	private Boolean diskPersistent = false;

	public CacheConfiguration() {
	}
	public CacheConfiguration(String cacheName,Integer maxElementsInMemory,
			Boolean eternal,Integer timeToIdleSeconds,Integer timeToLiveSeconds,
			Integer diskExpiryThreadIntervalSeconds) {
		this.cacheName = cacheName;
		this.maxElementsInMemory = maxElementsInMemory;
		this.eternal = eternal;
		this.timeToIdleSeconds = timeToIdleSeconds;
		this.timeToLiveSeconds = timeToLiveSeconds;
		this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
	}
	
	public String getCacheName() {
		return cacheName;
	}
	public void setCacheName(String cacheName) {
		this.cacheName = cacheName;
	}
	public Integer getMaxElementsInMemory() {
		return maxElementsInMemory;
	}
	public void setMaxElementsInMemory(Integer maxElementsInMemory) {
		this.maxElementsInMemory = maxElementsInMemory;
	}
	public Boolean getEternal() {
		return eternal;
	}
	public void setEternal(Boolean eternal) {
		this.eternal = eternal;
	}
	public Integer getTimeToIdleSeconds() {
		return timeToIdleSeconds;
	}
	public void setTimeToIdleSeconds(Integer timeToIdleSeconds) {
		this.timeToIdleSeconds = timeToIdleSeconds;
	}
	public Integer getTimeToLiveSeconds() {
		return timeToLiveSeconds;
	}
	public void setTimeToLiveSeconds(Integer timeToLiveSeconds) {
		this.timeToLiveSeconds = timeToLiveSeconds;
	}
	public Boolean getOverflowToDisk() {
		return overflowToDisk;
	}
	public void setOverflowToDisk(Boolean overflowToDisk) {
		this.overflowToDisk = overflowToDisk;
	}
	public Integer getMaxElementsOnDisk() {
		return maxElementsOnDisk;
	}
	public void setMaxElementsOnDisk(Integer maxElementsOnDisk) {
		this.maxElementsOnDisk = maxElementsOnDisk;
	}
	public Boolean getDiskPersistent() {
		return diskPersistent;
	}
	public void setDiskPersistent(Boolean diskPersistent) {
		this.diskPersistent = diskPersistent;
	}
	public Integer getDiskExpiryThreadIntervalSeconds() {
		return diskExpiryThreadIntervalSeconds;
	}
	public void setDiskExpiryThreadIntervalSeconds(
			Integer diskExpiryThreadIntervalSeconds) {
		this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
	}
	public String getMemoryStoreEvictionPolicy() {
		return memoryStoreEvictionPolicy;
	}
	public void setMemoryStoreEvictionPolicy(String memoryStoreEvictionPolicy) {
		this.memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
	}
	
	
	
}

 

 

package com.cache;

import java.io.Serializable;

/**
 * 缓存元素,所对应的属性
 */
@SuppressWarnings("serial")
public class Element implements Serializable {
	static final long ONE_SECOND = 1000L;
	private Object key;
	private Object value;
	// 使用次数
	private volatile long hitCount = 0;
	// 这些属性是单个元素的属性控制
	// 是否使用单独元素控制
	private Boolean isOpen = false;
	// 从创建时间开始后,还能存活时间,0 表示一直存活,超时时间=timeToLive + CreationTime
	private volatile int timeToLive = 0;
	// 从最近即(min(CreationTime,LastAccessTime)) 后还剩余的时间
	private volatile int timeToIdle = 0;
	// 创建时间
	private transient long creationTime;
	// 最后一次使用的时间
	private transient long lastAccessTime;
	// 最后更新时间
	private volatile long lastUpdateTime;
	// 表示是否使用cache 级别的控制,还是元素级别的控制,这里暂时不用
	private volatile boolean cacheDefaultLifespan = true;
	
	public Element(final Object key, final Object value){
		init(key,value);
	}
	
	public Element(final Object key, final Object value,Boolean isOpen){
		init(key, value);
		this.isOpen = isOpen;
	}
	
	private void init(final Object key, final Object value){
		this.key = key;
		this.value = value;
		this.creationTime = System.currentTimeMillis();
		this.lastAccessTime = System.currentTimeMillis();
	}
	
	
	public Object getKey() {
		return key;
	}
	public void setKey(Object key) {
		this.key = key;
	}
	public Object getValue() {
		return value;
	}
	public long getHitCount() {
		return hitCount;
	}
	public void setHitCount(long hitCount) {
		this.hitCount = hitCount;
	}
	public void addHitCount(){
		hitCount += 1;
	}

	public int getTimeToLive() {
		return timeToLive;
	}


	public void setTimeToLive(int timeToLive) {
		this.timeToLive = timeToLive;
	}


	public int getTimeToIdle() {
		return timeToIdle;
	}


	public void setTimeToIdle(int timeToIdle) {
		this.timeToIdle = timeToIdle;
	}
	public long getCreationTime() {
		return creationTime;
	}
	public void setCreationTime(long creationTime) {
		this.creationTime = creationTime;
	}
	public long getLastAccessTime() {
		return lastAccessTime;
	}
	
	public void setLastAccessTime(long lastAccessTime) {
		this.lastAccessTime = lastAccessTime;
	}
	public long getLastUpdateTime() {
		return lastUpdateTime;
	}
	public void setLastUpdateTime(long lastUpdateTime) {
		this.lastUpdateTime = lastUpdateTime;
	}


	public boolean isCacheDefaultLifespan() {
		return cacheDefaultLifespan;
	}


	public void setCacheDefaultLifespan(boolean cacheDefaultLifespan) {
		this.cacheDefaultLifespan = cacheDefaultLifespan;
	}


	public void setValue(Object value) {
		this.value = value;
	}
	
	

	 public Boolean getIsOpen() {
		return isOpen;
	}

	public void setIsOpen(Boolean isOpen) {
		this.isOpen = isOpen;
	}

	/**
	  * 判断元素 是否过期
	  * @return
	  */
	 public boolean isExpired() {
	        if (isEternal()) {
	            return false;
	        }
	        // 获得过期时间
	        long expirationTime = getExpirationTime();
	        long now = System.currentTimeMillis();
	        return now > expirationTime;
	 }
	 // 是否是不会过期
	 public boolean isEternal() {
	        return (0 == timeToIdle) && (0 == timeToLive);
	 }
	 
	 // 计算过期时间
	 public long getExpirationTime() {
	        if (isEternal()) {
	            return Long.MAX_VALUE;
	        }
	        // 存活时间
	        long expirationTime = 0;
	        long ttlExpiry = creationTime + getTimeToLive() * ONE_SECOND;
	        // 到期时间
	        long mostRecentTime = Math.max(creationTime, lastAccessTime);
	        long ttiExpiry = mostRecentTime + getTimeToIdle() * ONE_SECOND;
	        // 如果仅仅设置了timeToLive,那么时间以 timeToLive的计算为准
	        if (getTimeToLive() != 0 && (getTimeToIdle() == 0 || lastAccessTime == 0)) {
	            expirationTime = ttlExpiry;
	        } else if (getTimeToLive() == 0) {
	        	// 如果仅仅设置了 timeToIdle,那么时间以timeToIdle 的计算为准
	            expirationTime = ttiExpiry;
	        } else {
	        	// 如果两种都设置了,那么取小的一个为准
	            expirationTime = Math.min(ttlExpiry, ttiExpiry);
	        }
	        return expirationTime;
	    }
	 
	 // 刷新最后一次使用时间
	 public void refreshLastAccessTime(){
		 lastAccessTime = System.currentTimeMillis();
	 }
	
}

 

package com.cache;

import java.util.Iterator;
import java.util.Map;
/**
 * 检查的的一些方式
 * @author Administrator
 *
 */
public class CheckManager {

	protected CacheConfiguration configure;
	protected Map map;
	
	public CheckManager(CacheConfiguration configure,Map map) {
		this.map = map;
		this.configure = configure;
	}
	
	// 添加检查元素是否已经到达最大值,或者已经过期
	public Object[]  checkConfigure(int elementSize){
		int removeSize = map.size()+elementSize - configure.getMaxElementsInMemory();
		// 判断缓存是否已满
		if(removeSize > 0){
			// 按规则删除元素,这里不写磁盘
			if(!configure.getDiskPersistent()){
				return removeElementByEvictionType(removeSize);
			}
		}
		return null;
	}
	
	// 根据方式移除
	public Object[] removeElementByEvictionType(int removeSize){
		if(configure.getMemoryStoreEvictionPolicy().equals(EvictionType.LRU.name())){
			return removeElementByLRU(removeSize);
		}
		return null;
	}
	
	
	
	// 暂时默认根据最少使用次数进行删除
	private Object[] removeElementByLRU(int removeSize){
		Object keys[] = new Object[removeSize];
		long hits[] = new long[removeSize];
		Iterator it = map.keySet().iterator();
		// 找出hit值最小的 removeSize 个元素
		int index = 0;
		while(it.hasNext()){
			Object key =  it.next();
			Element e = map.get(key);
			long hit = e.getHitCount();
			if(index < removeSize){
				hits[index] = hit;
				keys[index] = key;
				index ++;
			}else{
				long pos = getMinIndex(hits, hit);
				if(pos >= 0){
					keys[(int) pos] = key;
				}
			}
		}
		return keys;
	}
	
	private long getMinIndex(long hits[],long hit){
		long pos = -1;
		for(int i = 0;i hit){
				hits[i] = hit;
				pos = i;;
			}
		}
		return pos;
	}

	
	
}

 

package com.cache;
/**
 * 几种删除策略
 */
public enum EvictionType {
	 LRU,LFU,FIFO
}

 

package com.cache;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 循环检查元素
 * @author Administrator
 *
 */
public class CacheListener extends Thread{
	
	private Cache cache;
	
	private volatile boolean stop = false;
	private volatile long ONE_SECOND = 1000;
	
	public boolean isStop() {
		return stop;
	}
	public void setStop(boolean stop) {
		this.stop = stop;
	}
	public CacheListener(Cache cache) {
		this.cache = cache;
	}
	
	@Override
	public void run() {
		long time = cache.getConfigure().getDiskExpiryThreadIntervalSeconds();
		try {
			while(!stop){
				sleep(time*ONE_SECOND);
				threadCheckElement();
			}
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}
	
	public void destory(){
		ONE_SECOND = 0;
		stop = true;
	}
	
	
	public void threadCheckElement(){
		List keys = new ArrayList();
		Map map  = cache.getAll();
		if(map != null && map.size() > 0){
			for(Entry e0: map.entrySet()){
				Element e = e0.getValue();
				if(e != null && e.isExpired()){
					keys.add(e0.getKey());
				}
			}
		}
		cache.removeAll(keys.toArray());
	}
	
	

}
 
   

 

package com.test;

import com.cache.Cache;
import com.cache.CacheConfiguration;
import com.cache.CacheManager;
import com.cache.Element;



public class Test {

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		CacheManager cm = CacheManager.getInstance();
		Cache c1 = new Cache(getConfigure());
		// 最大放5个
		putTestE1(c1);
		cm.putCache(c1);
		
		// 只有2 的hit 值最小,因此超过了就被移除了 null
		System.out.println(c1.get(2));
		// 想存放6了,实际数据只有5个
		System.out.println("总数:"+c1.size());
		// 休息3秒,然后使用
		Thread.sleep(1000*3);
		System.out.println("刷新:"+c1.get(1));
		// 然后继续休息
		Thread.sleep(1000*3);
		System.out.println("使用着的元素:"+c1.size());
		Thread.sleep(1000*15);
		System.out.println("时间太久,全部过期"+c1.size());
		
		//cm.shutDown();
		
	}
	
	public static CacheConfiguration getConfigure(){
		CacheConfiguration c = new CacheConfiguration();
		c.setCacheName("Test1");
		// 最多存放5个元素
		c.setMaxElementsInMemory(5);
		// 假设5秒不用就过期,这两个时间一般默认选小的一个执行,最长时间是 存活的总时间
		c.setTimeToIdleSeconds(5);
		// 假设最长能存活115秒
		c.setTimeToLiveSeconds(115);
		// 6秒 检查一次过期
		c.setDiskExpiryThreadIntervalSeconds(6);
		return c;
	}
	
	public static void putTestE1(Cache c1){
		c1.put(new Element(1, 2));
		c1.get(1);
		c1.put(new Element(2, 2));
		//c1.get(2);
		c1.put(new Element(3, 2));
		c1.get(3);
		c1.put(new Element(4, 2));
		c1.get(4);
		c1.put(new Element(5, 2));
		c1.get(5);
		c1.put(new Element(6, 2));
	}
	
	

}

 

小结:

         1.这里是看了一些Ehcache 的源码,加上自己的思路写的,仅做第一次尝试之用

         2.关于磁盘缓存 和 分布式 的东西这里是没有的,包括异常各种处理,这里都是没有的

         3.关于缓存管理,这里每个cache 都有一个线程,是需要改进的,比较消耗资源

         4.测试代码,我测试了一部分,会有遗漏的,留点有兴趣的去搞吧。

         5.有问题请多指点,代码,和ehcache 已经在里面的,可以参考。

你可能感兴趣的:(缓存)