`

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

阅读更多
使用软引用构建敏感数据的缓存


一、实现原理

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对象引用及对象赋值
分享到:
评论

相关推荐

    ASP.NET4高级程序设计第4版 带目录PDF 分卷压缩包 part1

    11.3 数据缓存 11.3.1 向缓存添加项目 11.3.2 简单的缓存测试 11.3.3 缓存优先级 11.3.4 使用数据源控件的缓存 11.4 缓存依赖 11.4.1 文件和缓存项目依赖 11.4.2 聚合依赖 11.4.3 移除项目回调 ...

    ASP.NET4高级程序设计(第4版) 3/3

    11.3 数据缓存 373 11.3.1 向缓存添加项目 373 11.3.2 简单的缓存测试 375 11.3.3 缓存优先级 376 11.3.4 使用数据源控件的缓存 376 11.4 缓存依赖 379 11.4.1 文件和缓存项目依赖 379 11.4.2 聚合...

    Hibernate+中文文档

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    Hibernate_3.2.0_符合Java习惯的关系数据库持久化

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    HibernateAPI中文版.chm

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    hibernate3.2中文文档(chm格式)

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    hibernate 体系结构与配置 参考文档(html)

    使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2. ...

    Hibernate参考文档

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2. ...

    Hibernate 中文 html 帮助文档

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2. ...

    Python Cookbook

    7.2 使用pickle和cPickle模块序列化数据 277 7.3 在Pickling的时候压缩 280 7.4 对类和实例使用cPickle模块 281 7.5 Pickling被绑定方法 284 7.6 Pickling代码对象 286 7.7 通过shelve修改对象 288 7.8 使用...

    PHP和MySQL Web开发第4版pdf以及源码

    16.2.1 访问或修改敏感数据 16.2.2 数据丢失或破坏 16.2.3 拒绝服务 16.2.4 恶意代码注入 16.2.5 服务器被攻破 16.3了解与我们“打交道”的用户 16.3.1 破解人员 16.3.2 受影响机器的未知情用户 16.3.3 对公司不满的...

    PHP和MySQL Web开发第4版

    16.2.1 访问或修改敏感数据 16.2.2 数据丢失或破坏 16.2.3 拒绝服务 16.2.4 恶意代码注入 16.2.5 服务器被攻破 16.3了解与我们“打交道”的用户 16.3.1 破解人员 16.3.2 受影响机器的未知情用户 16.3.3 对公司不满的...

    PHP和MySQL WEB开发(第4版)

    16.2.1 访问或修改敏感数据 16.2.2 数据丢失或破坏 16.2.3 拒绝服务 16.2.4 恶意代码注入 16.2.5 服务器被攻破 16.3了解与我们“打交道”的用户 16.3.1 破解人员 16.3.2 受影响机器的未知情用户 16.3.3 对公司不满的...

    Hibernate中文详细学习文档

    10.12. 使用元数据 11. 事务和并发 11.1. Session和事务范围(transaction scope) 11.1.1. 操作单元(Unit of work) 11.1.2. 长对话 11.1.3. 关注对象标识(Considering object identity) 11.1.4. 常见问题 11.2...

    asp.net知识库

    理解DataSet的数据缓存机制 存储过程 可按任意字段排序的分页存储过程(不用临时表的方法,不看全文会后悔) 常用sql存储过程集锦 存储过程中实现类似split功能(charindex) 通过查询系统表得到纵向的表结构 将数据库表...

    Web开发敏捷之道-应用Rails进行敏捷Web开发-第三版.rar

    19.5 自引用的连接 287 19.6 Acts As 288 19.7 何时保存 291 19.8 预先读取子记录 292 19.9 计数器 293 第20章 ActiveRecord第三部分:对象生命周期 295 20.1 校验 295 20.2 回调 303 20.3 高级属性 308 20.4 事务 ...

    C#微软培训资料

    16.2 使用名字空间 .200 16.3 使用指示符 .203 16.4 程 序 示 例 .206 16.5 小 结 .213 第十七章 文 件 操 作 .215 17.1 .Net 框架结构提供的 I/O 方式 .215 17.2 文件存储管理 .217 17.3 读 写 文 件...

Global site tag (gtag.js) - Google Analytics