`

使用Hashtable和线程技术制作自己简单的内存缓存

 
阅读更多
1. 首先,我们要做一个缓存数据类
  这个数据类要有一个数据成员,存放你的缓存数据,可以是一个类,也可以是一个List
  我们这里把它统一为Object
  然后要有一个过期时间,并且有一个访问次数,如果访问达到一定数量后,自动重置缓存

代码如下:
package zeus.cache.memory;

public class CacheData {
	private Object data;
	private long time;
	private int count;

	public CacheData() {

	}

	public CacheData(Object data, long time, int count) {
		this.data = data;
		this.time = time;
		this.count = count;
	}

	public CacheData(Object data) {
		this.data = data;
		this.time = System.currentTimeMillis();
		this.count = 1;
	}

	public void addCount() {
		count++;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

	public Object getData() {
		return data;
	}

	public void setData(Object data) {
		this.data = data;
	}

	public long getTime() {
		return time;
	}

	public void setTime(long time) {
		this.time = time;
	}
}


2.在有了缓存数据类以后,我们将建立一个缓存操作类

public class CacheOperation {
	private static CacheOperation singleton = null;

	private Hashtable cacheMap;// 存放缓存数据

	private ArrayList threadKeys;// 处于线程更新中的key值列表

	public static CacheOperation getInstance() {
		if (singleton == null) {
			singleton = new CacheOperation();
		}
		return singleton;
	}

	private CacheOperation() {
		cacheMap = new Hashtable();
		threadKeys = new ArrayList();
	}

     ...

}


我们看到这个缓存操作类是采用singleton模式的,因为我们的缓存可能要被多次调用,而
它只占用我们内存中的同一块空间,因为必须要采用singleton模式

增加缓存数据
/**
	 * 添加数据缓存 与方法getCacheData(String key, long intervalTime, int
	 * maxVisitCount)配合使用
	 * 
	 * @param key
	 * @param data
	 */
	public void addCacheData(String key, Object data) {
		addCacheData(key, data, true);
	}

	private void addCacheData(String key, Object data, boolean check) {
		if (Runtime.getRuntime().freeMemory() < 5L * 1024L * 1024L) {// 虚拟机内存小于10兆,则清除缓存
			// log.warn("WEB缓存:内存不足,开始清空缓存!");
			removeAllCacheData();
			return;
		} else if (check && cacheMap.containsKey(key)) {
			// log.warn("WEB缓存:key值= " + key + " 在缓存中重复, 本次不缓存!");
			return;
		}
		cacheMap.put(key, new CacheData(data));
	}


缓存获取机制就比较复杂,我们要判断该缓存是否过期,如果过期,就需要用后台线程重新获取
最新的缓存数据,而获取缓存数据的机制将是一个方法调用,这里方法可以通过反射来传进去

/**
	 * 些类存放方法的主调对像,名称及参数数组
	 * 
	 * @author master haku
	 * 
	 */
	public class MethodInfo {
		private Object o;
		private String methodName;
		private Object[] parameters;

		public MethodInfo(Object o, String methodName, Object[] parameters) {
			this.o = o;
			this.methodName = methodName;
			this.parameters = parameters;
		}

		public String getMethodName() {
			return methodName;
		}

		public void setMethodName(String methodName) {
			this.methodName = methodName;
		}

		public Object getO() {
			return o;
		}

		public void setO(Object o) {
			this.o = o;
		}

		public Object[] getParameters() {
			return parameters;
		}

		public void setParameters(Object[] parameters) {
			this.parameters = parameters;
		}

		public String toString() {
			StringBuffer str = new StringBuffer(methodName);
			if (parameters != null) {
				str.append("(");
				for (int i = 0; i < parameters.length; i++) {
					if (parameters[i] instanceof Object[]) {
						str.append(Arrays.toString((Object[]) parameters[i])).append(",");
					} else {
						str.append(parameters[i]).append(",");
					}
				}
				str.append(")");
			}
			return str.toString();
		}
	}


/**
	 * 线程调用方法
	 * 
	 * @author master haku
	 * 
	 */
	private class InvokeThread extends Thread {
		private Object o;
		private String methodName;
		private Object[] parameters;
		private String key;

		public InvokeThread(Object o, String methodName, Object[] parameters,
				String key) {
			this.o = o;
			this.methodName = methodName;
			this.parameters = parameters;
			this.key = key;
		}

		public void run() {
			threadKeys.add(key);
			invoke(o, methodName, parameters, key);
			threadKeys.remove(key);
		}
	}


/**
	 * 找不到完全匹配的方法时,对参数进行向父类匹配 因为方法aa(java.util.List) 与
	 * aa(java.util.ArrayList)不能自动匹配到
	 * 
	 * @param oc
	 * @param methodName
	 * @param pcs
	 * @return
	 * @throws NoSuchMethodException
	 * @throws NoSuchMethodException
	 */
	private Method matchMethod(Class oc, String methodName, Class[] pcs)
			throws NoSuchMethodException, SecurityException {
		try {
			Method method = oc.getDeclaredMethod(methodName, pcs);
			return method;
		} catch (NoSuchMethodException e) {
			Method[] ms = oc.getDeclaredMethods();
			aa: for (int i = 0; i < ms.length; i++) {
				if (ms[i].getName().equals(methodName)) {
					Class[] pts = ms[i].getParameterTypes();
					if (pts.length == pcs.length) {
						for (int j = 0; j < pts.length; j++) {
							if (!pts[j].isAssignableFrom(pcs[j])) {
								break aa;
							}
						}
						return ms[i];
					}
				}
			}
			throw new NoSuchMethodException();
		}
	}


更新缓存数据

/**
	 * 递归调用给定方法更新缓存中数据据
	 * 
	 * @param o
	 * @param methodName
	 * @param parameters
	 * @param key
	 * @return 若反射调用方法返回值为空则返回该值的类型
	 */
	private Object invoke(Object o, String methodName, Object[] parameters,
			String key) {
		Object returnValue = null;
		try {
			Class[] pcs = null;
			if (parameters != null) {
				pcs = new Class[parameters.length];
				for (int i = 0; i < parameters.length; i++) {
					if (parameters[i] instanceof MethodInfo) {// 参数类型是MethodInfo则调用该方法的返回值做这参数
						MethodInfo pmi = (MethodInfo) parameters[i];
						Object pre = invoke(pmi.getO(), pmi.getMethodName(),
								pmi.getParameters(), null);
						parameters[i] = pre;
					}
					if (parameters[i] instanceof Class) {
						pcs[i] = (Class) parameters[i];
						parameters[i] = null;
					} else {
						pcs[i] = parameters[i].getClass();
					}
				}
			}
			Class oc = o instanceof Class ? (Class) o : o.getClass();
			// Method m = oc.getDeclaredMethod(methodName, pcs);
			Method m = matchMethod(oc, methodName, pcs);
			returnValue = m.invoke(o, parameters);
			if (key != null && returnValue != null) {
				addCacheData(key, returnValue, false);
			}
			if (returnValue == null) {
				returnValue = m.getReturnType();
			}
		} catch (Exception e) {
			// log.error("调用方法失败,methodName=" + methodName);
			if (key != null) {
				removeCacheData(key);
				// log.error("更新缓存失败,缓存key=" + key);
			}
			e.printStackTrace();
		}
		return returnValue;
	}


根据键值获取缓存数据

/**
	 * 当缓存中数据失效时,用不给定的方法线程更新数据
	 * 
	 * @param o
	 *          取得数据的对像(该方法是静态方法是不用实例,则传Class实列)
	 * @param methodName
	 *          该对像中的方法
	 * @param parameters
	 *          该方法的参数列表(参数列表中对像都要实现toString方法,若列表中某一参数为空则传它所属类的Class)
	 * @param intervalTime
	 *          缓存的时间周期,小于等于0时不限制
	 * @param maxVisitCount
	 *          访问累积次数,小于等于0时不限制
	 * @return
	 */
	public Object getCacheData(Object o, String methodName, Object[] parameters,
			long intervalTime, int maxVisitCount) {
		Class oc = o instanceof Class ? (Class) o : o.getClass();
		StringBuffer key = new StringBuffer(oc.getName());// 生成缓存key值
		key.append("-").append(methodName);
		if (parameters != null) {
			for (int i = 0; i < parameters.length; i++) {
				if (parameters[i] instanceof Object[]) {
					key.append("-").append(Arrays.toString((Object[]) parameters[i]));
				} else {
					key.append("-").append(parameters[i]);
				}
			}
		}

		CacheData cacheData = (CacheData) cacheMap.get(key.toString());
		if (cacheData == null) {// 等待加载并返回
			Object returnValue = invoke(o, methodName, parameters, key.toString());
			return returnValue instanceof Class ? null : returnValue;
		}
		if (intervalTime > 0
				&& (System.currentTimeMillis() - cacheData.getTime()) > intervalTime) {
			daemonInvoke(o, methodName, parameters, key.toString());// 缓存时间超时,启动线程更新数据
		} else if (maxVisitCount > 0 && (maxVisitCount - cacheData.getCount()) <= 0) {// 访问次数超出,启动线程更新数据
			daemonInvoke(o, methodName, parameters, key.toString());
		} else {
			cacheData.addCount();
		}
		return cacheData.getData();
	}


/**
	 * 移除缓存中的数据
	 * 
	 * @param key
	 */
	public void removeCacheData(String key) {
		cacheMap.remove(key);
	}

	/**
	 * 移除所有缓存中的数据
	 * 
	 */
	public void removeAllCacheData() {
		cacheMap.clear();
	}


如何调用

先做一个缓存服务类
public class GtaHotelCacheService {
	public List<TGtahotel> getAllHotelList() {
		GtaHotelDao_Hib hotelDao = new GtaHotelDao_Hib();
		CacheOperation co = CacheOperation.getInstance();
		Object hotels = co.getCacheData(hotelDao, "getHotelList", new Object[]{},  3600*1000, 0);
		
		return (List<TGtahotel>)hotels;
	}
}


在控制台中测试,由于要体现缓存的价值,因为我们需要一个循环
这个循环根据用户选择才退出

public class ZeusCacheTest {
	public static void main(String[] args) {
		Scanner in;
		String key = "Y";
		PerformanceManager perf = null;
		
		while (key.toUpperCase().equals("Y")) {
			/* -- Performance Test Start -- */
	    perf = new PerformanceManager("Gta Hotel From Cache", new TimeConsole());
	    perf.Start();
	    /* -- Performance Test Start -- */
	    
			GtaHotelCacheService service = new GtaHotelCacheService();
			List<TGtahotel> hotels = service.getAllHotelList();
	
			displayHotels(hotels);
			
			/* -- Performance Test End -- */
	    perf.Stop();
	    perf.PrintTime();
	    /* -- Performance Test End -- */
	    
	    in = new Scanner(System.in);
			System.out.println("Try Again(Y/N): ");
			key = in.nextLine();
		}
	}

	private static void displayHotels(List<TGtahotel> hotels) {
    System.out.println("There are " + hotels.size() + " records.");
	}
}


输出结果:
There are 52973 records.
******************************* Performance Log *******************************
Module: Gta Hotel From Cache, Time Consumed: 0h:0m:6s:836ms
*******************************************************************************
Try Again(Y/N):
y
There are 52973 records.
******************************* Performance Log *******************************
Module: Gta Hotel From Cache, Time Consumed: 0h:0m:0s:0ms
*******************************************************************************

分享到:
评论

相关推荐

    Java并发编程相关源码集 包括多任务线程,线程池等.rar

     volatile关键字的非原子性、volatile关键字的使用、AtomicInteger原子性操作、线程安全小例子:多个线程竞争问题、多个线程多个锁问题、创建一个缓存的线程池、多线程使用Vector或者HashTable的示例(简单线程同步...

    Java面试总结,Redis宕机数据丢失解决方案,看完这篇彻底明白了.docx

    * HashTable和HashMap的区别在于,HashTable是线程安全的,而HashMap不是 2. ConcurrentHashMap的实现和原理 * ConcurrentHashMap使用分段锁来实现线程安全, 每个分段锁对应一个链表,通过hash函数将键映射到对应...

    .net性能优化宝典

    2.3.1 使用ExecuteScalar和ExecuteNonQuery. 19 2.3.2 使用Prepare. 19 2.3.3 使用绑定变量 ★... 19 2.4 DataReader 20 2.4.1 显式关闭DataReader 20 2.4.2 用索引号访问代替名称索引号访问属性... 20 2.4.3 ...

    java面试笔试资料包括JAVA基础核心知识点深度学习Spring面试题等资料合集.zip

    关于线程和线程池的学习与使用.docx 深入理解JVM垃圾回收机制.docx 深入理解多线程实现的另一种方式Callable.docx 红黑树简介.docx 线程死锁及解决办法.docx 线程锁之重入锁.docx 线程间的通信.docx 虚拟机内存结构...

    Java开发技术大全 电子版

    8.3.3使用isAlive()和join()等待子线程结束273 8.3.4设置线程优先级275 8.4线程的通信与协调277 8.4.1线程的互斥277 8.4.2线程的同步279 8.4.3暂停、恢复和停止线程282 8.4.4生产者-消费者问题实例284 8.5本...

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    【多线程】简述synchronized 和java.util.concurrent.locks.Lock的异同? 90 【线程】ThreadLocal的作用 90 【Spring】什么是IOC和DI?DI是如何实现的 91 【Spring】spring中的IOC(控制反转)的原理 92 【Spring】...

    涵盖了90%以上的面试题

    Java中的异常处理机制的简单原理和应用。 java socket java序列化 JVM加载class文件的原理 双亲委派模型 为什么要自定义类加载器 如何自定义类加载器 什么是GC 内存泄漏和内存溢出 Java的内存模型(JVM的内存划分) ...

    【Java面试+Java学习指南】 一份涵盖大部分Java程序员所需要掌握的核心知识

    Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理 Java集合详解2:Queue和LinkedList Java集合详解3:Iterator,fail-fast机制与比较器 Java集合详解4:HashMap和HashTable Java集合详解5:深入...

    java面试宝典

    31、java 中会存在内存泄漏吗,请简单描述。 11 32、abstract 的method 是否可同时是static,是否可同时是native,是否可同时是synchronized? 11 33、静态变量和实例变量的区别? 11 34、是否可以从一个static 方法...

    2021年最新java面试题--视频讲解(内部培训84个知识点超详细).rar

    Java面试题11.HashMap和HashTable的区别 Java面试题12.实现一个拷贝文件的工具类要使用字节流还是字符串 Java面试题13.线程的的实现方式?怎么启动线程?怎么区分线程? Java面试题14.线程并发库和线程池的作用 Java...

    JAVA高级工程师笔试题(技术部分).pdf

    7. 集合框架:ArrayList 和 HashSet 的区别,HashMap 和 Hashtable 的区别?(5 分) 知识点:集合框架是 Java 语言的重要组成部分,考察的是候选人的集合框架知识和编程能力。解决这个问题需要候选人具备良好的...

    千方百计笔试题大全

    31、java 中会存在内存泄漏吗,请简单描述。 11 32、abstract 的method 是否可同时是static,是否可同时是native,是否可同时是synchronized? 11 33、静态变量和实例变量的区别? 11 34、是否可以从一个static 方法...

    Java常见面试题208道.docx

    21.HashMap 和 Hashtable 有什么区别? 22.如何决定使用 HashMap 还是 TreeMap? 23.说一下 HashMap 的实现原理? 24.说一下 HashSet 的实现原理? 25.ArrayList 和 LinkedList 的区别是什么? 26.如何实现数组和 ...

    疯狂JAVA讲义

    7.6.1 HashMap和Hashtable实现类 271 7.6.2 SortedMap接口和TreeMap实现类 276 7.6.3 WeakHashMap实现类 279 7.6.4 IdentityHashMap实现类 280 7.6.5 EnumMap实现类 281 7.7 HashSet和HashMap的性能选项 282 ...

    java面试题

    8. HashMap和Hashtable的区别。 10 9. final, finally, finalize的区别。 10 10. sleep() 和 wait() 有什么区别? 10 11. Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型? 10 12. error和...

    asp.net知识库

    制作一个简单的多页Tab功能 一完美的关于请求的目录不存在而需要url重写的解决方案! 在C#中实现MSN消息框的功能 XmlHttp实现无刷新三联动ListBox 鼠标放在一个连接上,会显示图片(类似tooltip) 使用microsoft.web.ui...

    Java范例开发大全 (源程序)

     实例154 使用静态成员变量计算内存中实例化的对象数目 239  实例155 实现加减乘除的方法 240  8.3 面向对象的设计模式 241  实例156 Singleton单例模式 242  实例157 招聘(简单工厂模式) 243  实例158...

    java范例开发大全(pdf&源码)

    实例154 使用静态成员变量计算内存中实例化的对象数目 239 实例155 实现加减乘除的方法 240 8.3 面向对象的设计模式 241 实例156 Singleton单例模式 242 实例157 招聘(简单工厂模式) 243 实例158 同学聚会(工厂...

    java范例开发大全源代码

     实例154 使用静态成员变量计算内存中实例化的对象数目 239  实例155 实现加减乘除的方法 240  8.3 面向对象的设计模式 241  实例156 Singleton单例模式 242  实例157 招聘(简单工厂模式) 243  ...

    java范例开发大全

    实例154 使用静态成员变量计算内存中实例化的对象数目 239 实例155 实现加减乘除的方法 240 8.3 面向对象的设计模式 241 实例156 Singleton单例模式 242 实例157 招聘(简单工厂模式) 243 实例158 同学聚会(工厂...

Global site tag (gtag.js) - Google Analytics