使用软引用构建敏感数据的缓存

使用软引用构建敏感数据的缓存


一、实现原理

1.应用场景
查询频率较高的数据;每次查询均需要通过接口与数据库交互,构建对象仍需要占用一部分内存;即便上次查询的结果仍在内存中还未被GC回收但仍需要再次进行相同的查询操作;
将查询结果放入内存--大量占用内存空间,增加发生内存溢出的可能;
每次都重新查询--当前的查询结果使用完毕后,实现的缺陷在于即使垃圾收集线程还没有进行垃圾收集,包含雇员档案信息的对象仍然完好地保存在内存中,应用程序也要重新构建一个对象
折中的方法,能重新获取那些尚未被回收的Java对象的引用,必将减少不必要的访问,大大提高程序的运行速度

2.软引用的特点
对于软引用关联着的对象,如果内存充足,则垃圾回收器不会回收该对象,如果内存不够了,就会回收这些对象的内存

3.软引用配合引用队列
软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

二、代码实现

实现思想
  • 工具类使用懒汉模式的单例实现,避免多线程调用时出现问题,对外接口使用synchronized 关键字修饰
  • 构建缓存,使用Hashtable,key:存储内容的唯一标识,value:存储对象的软引用实现;取元素时,若此缓存中存在,则说明此时对象未被回收、或已被回收,但软引用在引用队列中未被清除;若无,说明存储的对象已被GC;
  • 构建软引用队列,泛型 T 为要存储的对象,当软引用所依赖的对象被GC回收后,JVM将此软引用加入到与之关联的引用队列中。即,此时在等待GC到达 out of memory 前回收此时占用的内存,故每次放入新的对象前,先判断此队列是否有值,若有,主动释放所占用的内存空间



/**
 * 员工信息类
 */
public class Employee {
	
	private String id ; // 主键

	private String name ; // 姓名
	
	private String department ; // 部门
	
	private Double salary ; // 工资

	public Employee(String id){
		this.id = id ;
	}
	
	public Employee(String id, String name, String department, Double salary) {
		super();
		this.id = id;
		this.name = name;
		this.department = department;
		this.salary = salary;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDepartment() {
		return department;
	}

	public void setDepartment(String department) {
		this.department = department;
	}

	public Double getSalary() {
		return salary;
	}

	public void setSalary(Double salary) {
		this.salary = salary;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", department="
				+ department + ", salary=" + salary + "]";
	}
}






import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Hashtable;

/**
 * 员工信息缓存
 */
public class EmployeeCache {

	// 单例模式:懒汉式
	private static EmployeeCache employeeCache ;
	
	private EmployeeCache(){
		// 构造缓存对象时,初始化缓存容器、软索引队列
		cache = new Hashtable<String, EmployeeCache.EmployeeRef>();
		queue = new ReferenceQueue<Employee>();
	}
	
	public synchronized static EmployeeCache getInstance(){
		if(null == employeeCache){
			employeeCache = new EmployeeCache();
		}
		return employeeCache;
	}
	
	// 缓存容器
	private static Hashtable<String,EmployeeRef> cache = null;
	// 软索引队列
	private static ReferenceQueue<Employee> queue = null ;
	
	/**
	 * 私有内部类:Employee 的软索引对象
	 */
	private class EmployeeRef extends SoftReference<Employee>{
		private String uniqueKey = "";
		public EmployeeRef(Employee referent, ReferenceQueue<? super Employee> q) {
			super(referent, q);
			this.setUniqueKey(referent.getId());
		}
		public void setUniqueKey(String uniqueKey) {
			this.uniqueKey = uniqueKey;
		}
	}
	
	/**
	 * 向缓存中添加元素
	 */
	public void put(Employee employee){
		// 清空已在引用队列中的软索引对象,释放空间
		clearReferenceQueue();
		EmployeeRef ref = new EmployeeRef(employee, queue);
		cache.put(ref.uniqueKey, ref);
	}
	
	private void clearReferenceQueue(){
		EmployeeRef ref = null ;
		// 引用队列中的数据出队列
		while((ref = (EmployeeRef)queue.poll()) != null){
			// 同时清除该软引用作为KEY的对象内容
			cache.remove(ref.uniqueKey);
		}
	}
	
	/**
	 * 从缓存中取出元素
	 * @param key
	 * @return
	 */
	public Employee get(String key){
		
		Employee employee = null ;
		if(cache.containsKey(key)){
			EmployeeRef employeeRef = cache.get(key);
			employee = employeeRef.get();
		}
		if(null == employee){
			employee = new Employee(key);
			EmployeeRef ref = new EmployeeRef(employee, queue);
			cache.put(key, ref);
		}
		return employee ;
	}
}




public class EmployeeCacheMain {

	private static EmployeeCache cache = EmployeeCache.getInstance();
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Employee e1 = new Employee("1", "张三", "测试部门", 10000.0);
		Employee e2 = new Employee("2", "李四", "开发部门", 15000.0);
		cache.put(e1);
		cache.put(e2);
		
		Employee employee = cache.get("1");
		System.out.println(employee.toString());
		e2.setDepartment("测试部门");
		cache.put(e2);
		System.out.println(cache.get("2").getDepartment());
	}

}



博文参考:
java引用类型
浅谈java对象引用及对象赋值

猜你喜欢

转载自mingyundezuoan.iteye.com/blog/2400011