论坛首页 Java企业应用论坛

缓存同步策略重构

浏览 11014 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-11-23  
简述一下CommonTemplate(http://www.commontemplate.org)的模板工厂每次获取模板的过程如下:

检查内存缓存中是否存在,
若不存在,则检查持久化缓存中是否存在,
若还不存在,则重新解析模板并将模板压入内存缓存及持久化缓存,
若存在,则检查是否需要热加载,
若需要热加载,则对比文件是否已更改,
若已更改,则重新解析模板并将模板压入内存缓存及持久化缓存,
返回模板

因为很多检测点,开始整个过程都被同步执行了,

huangyh讨论时,
他提出,如果去掉同步,会出现什么?
总结是:
一、在并发下,同一模板会多次被解析和缓存,
二、非同步缓存容器保存时可能出错,

讨论后发现:
第一种影响对程序的正确性没什么影响的,只是“觉得”多次解析和缓存会影响性能,
而实际上,这些影响性能比同步块对引擎活性的影响要小很多,
即然如此,那只要保证缓存容器自身是线程安全的,就没必要同步整个获取过程。

缓存容器采用的是策略模式,
引擎留出的SPI是:org.commontemplate.config.Cache接口,重构后:
第一种方式:
在Cache接口的文档中声明,Cache的实现者都必须线程安全,即自行实现同步。
第二种方式:
引擎在调用Cache之前,使用装饰器模式统一外包装实现同步。
第三种方式:
结合上面两种方式,再加一个是否需要外部同步的标识性接口,引擎基于此判断是否需要外部同步。
   发表时间:2007-11-23  
可以以资源对象为锁对象进行同步,可以参考Tomcat的WebAppClassLoader的加载实现:

static final Object lock=new Object();
		
Resource entity=null;
entity=cache.get(name);
if(entity!=null)return entity;

sychonize(lock){

	entity=cache.get(name);

	if(entity==null){
		entity=new Resource();
		cache.put(name,entity);
	}

}

sychonize(entity){
         if(entity!=null && entity.getResource()!=null)return entity;

	Template template=load();
	entity.setResource(template);

}


这样应该是安全的,大家再看看有没更好的写法
0 请登录后投票
   发表时间:2007-11-23  
var res = getResoure(key);
if(res){
  return res;
}


sych(globalLock){
  var resourceLock = requireLock(key);
}
sych(resourceLock ){
  res = getResoure(key);
  if(res){
    return res;
  }
  res = buildResoure(key);
  putResoure(key,res);
  return res;
}


这样如何?
0 请登录后投票
   发表时间:2007-11-23  
楼上两位好想法!

按单实例分别开锁,可以减少很大同步面,且缓存容器可以不同步。

leadyu将template包装成Resource,再拿Resource作为锁
jindw将锁放在一个集合内,用同一个key却取锁,与template完全分离

哪一种会更有优势?

那是否可以直接用template(被缓存对象)本身作为锁?
看来是不行,template在锁之前不存在。
0 请登录后投票
   发表时间:2007-11-23  
javatar 写道
楼上两位好想法!

按单实例分别开锁,可以减少很大同步面,且缓存容器可以不同步。

leadyu将template包装成Resource,再拿Resource作为锁
jindw将锁放在一个集合内,用同一个key却取锁,与template完全分离

哪一种会更有优势?

那是否可以直接用template(被缓存对象)本身作为锁?
用Resource包装,主要为了保证entity和lock的缓存策略一致性,避免,一个存在一个不存在。

因为lock集合也必须有缓存策略,不能无限增大
0 请登录后投票
   发表时间:2007-11-23  
leadyu 写道
javatar 写道
楼上两位好想法!
。。。。
用Resource包装,主要为了保证entity和lock的缓存策略一致性,避免,一个存在一个不存在。

因为lock集合也必须有缓存策略,不能无限增大

leadyu
现在的想法已经更加完善了

不过,lock集合我认为就不需要缓存策略了,区区一个简单对象,由他去把。
0 请登录后投票
   发表时间:2007-11-23  
有2点想法:
A. 内存缓存及持久化缓存不应该作为2种缓存,缓存在哪里应该由Cache的实现者处理,而不是Cache的使用者就提前设计好的,比如Ehcache,OSCache都提供了基于内存和文件系统持久化的缓存。

B. 作为模板语言的解析结果缓存,可以不加同步,我觉得这一点要求也不是必须的:
javatar 写道

在Cache接口的文档中声明,Cache的实现者都必须线程安全,即自行实现同步。

比如我用非线程安全的HashMap来实现一个简单的内存缓存,获取缓存模板方法改成失效清除的策略:
1. 检查缓存中是否存在
2. 若存在,则检查是否需要热加载
3. 若需要热加载,则对比文件是否已更改
4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存
6. 返回模板
0 请登录后投票
   发表时间:2007-11-23  
jindw 的策略不错.呵呵
0 请登录后投票
   发表时间:2007-11-24  
Quake Wang 写道

A. 内存缓存及持久化缓存不应该作为2种缓存,缓存在哪里应该由Cache的实现者处理,而不是Cache的使用者就提前设计好的,比如Ehcache,OSCache都提供了基于内存和文件系统持久化的缓存。

这个肯定,引擎只会留出一个Cache的SPI,不会决定什么内存缓存和持久化缓存,
上面说的只是现在内置的Cache实现:持久化缓存包装内存缓存(Decorator方式),他们都实现了Cahce接口,只是组成了链,可以被替换掉。

Quake Wang 写道

4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存

第5步会不会出现并发修改异常?

0 请登录后投票
   发表时间:2007-11-24  
javatar 写道

Quake Wang 写道

4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存

第5步会不会出现并发修改异常?

这依赖于Cache的实现,成熟的Cache解决方案都会在put方法内部解决并发问题。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics