`

使用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
*******************************************************************************

分享到:
评论

相关推荐

    hashtable和hashmap的区别

    - **Hashtable**: 使用内部同步机制来确保线程安全,这意味着在执行关键操作时会锁定整个`Hashtable`对象。这种全局锁会导致较高的并发成本,特别是在高负载下。 - **HashMap**: 非同步设计使得其在单线程环境下的...

    JAVA_WEB_缓存技术

    总的来说,这个简单的缓存实现包含了一个基本的数据结构和基本的线程安全措施。然而,实际的Java Web缓存解决方案,如使用EhCache、Redis或Spring Cache等,通常会提供更复杂的功能,包括缓存失效策略、分布式缓存...

    分布式缓存服务器memcacaed的源代码

    分布式缓存服务器Memcached是互联网应用中广泛使用的内存对象缓存系统,用于减轻数据库的负载,提高应用程序的性能。Memcached的设计目标是简单且高效,它通过在内存中存储数据来提供快速的数据访问。让我们深入了解...

    java多线程技术整理

    Java多线程技术是Java编程中的重要组成部分,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,多线程主要通过`Thread`类、实现`Runnable`接口或`Callable`接口以及使用`ExecutorService`...

    c#重写HashTable

    在C#编程中,`HashTable`是一个非泛型集合类,它在.NET Framework早期版本中被广泛使用。然而,随着.NET Framework的不断发展,`HashTable`逐渐被更安全、类型安全且性能更高的`Dictionary, TValue&gt;`所取代。尽管...

    三大缓存比较

    3. **结构简单**:可以理解为一个大型的内存 HashTable,支持 key-value 键值对缓存。 4. **高可用性**:通过分布式架构,可以在多个节点之间分散数据,提升系统整体的可用性和性能。 5. **简单配置**:配置简单,...

    JVM性能优化:线程锁优化.docx

    - 线程有自己的工作内存,存储从主内存拷贝的变量副本。在多核处理器下,工作内存通常存储在高速缓存中,以提高访问速度。 2. **主内存与工作内存**: - 主内存是所有线程共享的区域,存储对象的属性值,位于堆...

    java的hashtable的用法.pdf

    `HashMap`通常比`Hashtable`更快,因为它不保证线程安全,但在多线程环境下,你需要自己处理同步问题。 - `Hashtable`不允许`null`键和`null`值,而`HashMap`允许一个`null`键和任意数量的`null`值。 - `Hashtable...

    HashMapvs.TreeMapvs.Hashtable

    `Hashtable`不允许null键和null值,并且它的实现方式相对过时,现在通常推荐使用`Collections.synchronizedMap()`将`HashMap`转换为线程安全的版本,或者使用`ConcurrentHashMap`,后者在多线程环境下的性能更好。...

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

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

    9、缓存(10题)1

    以上内容详细阐述了缓存技术中的Redis客户端并发控制、哈希表实现、一致性哈希算法、LRU策略、slab分配以及缓存系统的热点问题与解决方法,以及Memcached和Redis的基本特性比较。这些知识点对于理解和使用分布式缓存...

    java线程例子大全

    Java线程是并发编程的核心部分,它允许程序同时执行多个任务,从而提高系统效率和响应速度。...以上知识点涵盖了Java线程的基本概念、操作和高级特性,通过这些例子,开发者可以深入理解并熟练运用Java的并发编程技术。

    09、缓存(10题)1

    同时,对于Java的Hashtable,还需要考虑到线程安全,可能需要在多线程环境下使用同步机制,如`synchronized`关键字。 3. **一致性哈希** 一致性哈希是一种分布式哈希算法,主要用于分布式缓存系统,如Memcached。...

    java的一些基础知识什么这类的

    + 线程安全性:使用锁分段技术,将整个哈希表分成多个段,每个段都有自己的锁。 + 分段锁:每个段都是一个独立的哈希表,具有自己的锁。 + 原子操作:一些操作是原子的,例如 putIfAbsent、remove、replace 等。 ...

    在Java中运用Hashtable.doc

    - **HashMap** 不是线程安全的,但它允许使用`null`值和`null`键,而Hashtable不允许。 使用`Hashtable`或`HashMap`时,可以将一个键(key)和一个值(value)关联起来,并通过`put()`方法将键值对添加到表中。之后...

    Java实现LRU缓存的实例详解

    LRU缓存是一种常用的缓存策略,其主要思想是将最近使用的数据存储在缓存中,以便快速访问,而将不常用的数据从缓存中删除,以释放内存空间。 在Java中,可以使用LinkedHashMap实现LRU缓存。LinkedHashMap是一个双向...

    Redis大厂常见面试题

    本文将深入探讨Redis的使用场景、优势、单线程模型及其优化策略,以及其五种核心数据类型和底层数据结构。 1. **Redis的使用场景及缓存原因** - **用户登录信息缓存**:登录成功后,将用户信息存储在Redis中,减少...

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

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

    .net性能优化宝典

    - **使用对象池**:对于频繁创建和销毁的对象,可以采用对象池技术,预先创建一定数量的对象并进行复用,以减少GC压力。 **1.1.2 不要使用空析构函数** 析构函数的目的是为了释放非托管资源,但如果使用不当,则...

Global site tag (gtag.js) - Google Analytics