锁定老帖子 主题:缓存同步策略重构
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-11-23
http://www.commontemplate.org)的模板工厂每次获取模板的过程如下:
简述一下CommonTemplate(检查内存缓存中是否存在, 若不存在,则检查持久化缓存中是否存在, 若还不存在,则重新解析模板并将模板压入内存缓存及持久化缓存, 若存在,则检查是否需要热加载, 若需要热加载,则对比文件是否已更改, 若已更改,则重新解析模板并将模板压入内存缓存及持久化缓存, 返回模板 因为很多检测点,开始整个过程都被同步执行了, 和huangyh讨论时, 他提出,如果去掉同步,会出现什么? 总结是: 一、在并发下,同一模板会多次被解析和缓存, 二、非同步缓存容器保存时可能出错, 讨论后发现: 第一种影响对程序的正确性没什么影响的,只是“觉得”多次解析和缓存会影响性能, 而实际上,这些影响性能比同步块对引擎活性的影响要小很多, 即然如此,那只要保证缓存容器自身是线程安全的,就没必要同步整个获取过程。 缓存容器采用的是策略模式, 引擎留出的SPI是:org.commontemplate.config.Cache接口,重构后: 第一种方式: 在Cache接口的文档中声明,Cache的实现者都必须线程安全,即自行实现同步。 第二种方式: 引擎在调用Cache之前,使用装饰器模式统一外包装实现同步。 第三种方式: 结合上面两种方式,再加一个是否需要外部同步的标识性接口,引擎基于此判断是否需要外部同步。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间: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); } 这样应该是安全的,大家再看看有没更好的写法 |
|
返回顶楼 | |
发表时间: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; } 这样如何? |
|
返回顶楼 | |
发表时间:2007-11-23
楼上两位好想法!
按单实例分别开锁,可以减少很大同步面,且缓存容器可以不同步。 leadyu将template包装成Resource,再拿Resource作为锁 jindw将锁放在一个集合内,用同一个key却取锁,与template完全分离 哪一种会更有优势? 那是否可以直接用template(被缓存对象)本身作为锁? 看来是不行,template在锁之前不存在。 |
|
返回顶楼 | |
发表时间:2007-11-23
javatar 写道 楼上两位好想法!
用Resource包装,主要为了保证entity和lock的缓存策略一致性,避免,一个存在一个不存在。
按单实例分别开锁,可以减少很大同步面,且缓存容器可以不同步。 leadyu将template包装成Resource,再拿Resource作为锁 jindw将锁放在一个集合内,用同一个key却取锁,与template完全分离 哪一种会更有优势? 那是否可以直接用template(被缓存对象)本身作为锁? 因为lock集合也必须有缓存策略,不能无限增大 |
|
返回顶楼 | |
发表时间:2007-11-23
leadyu 写道 javatar 写道 楼上两位好想法!
用Resource包装,主要为了保证entity和lock的缓存策略一致性,避免,一个存在一个不存在。
。。。。 因为lock集合也必须有缓存策略,不能无限增大 leadyu 现在的想法已经更加完善了 不过,lock集合我认为就不需要缓存策略了,区区一个简单对象,由他去把。 |
|
返回顶楼 | |
发表时间:2007-11-23
有2点想法:
A. 内存缓存及持久化缓存不应该作为2种缓存,缓存在哪里应该由Cache的实现者处理,而不是Cache的使用者就提前设计好的,比如Ehcache,OSCache都提供了基于内存和文件系统持久化的缓存。 B. 作为模板语言的解析结果缓存,可以不加同步,我觉得这一点要求也不是必须的: javatar 写道 在Cache接口的文档中声明,Cache的实现者都必须线程安全,即自行实现同步。 比如我用非线程安全的HashMap来实现一个简单的内存缓存,获取缓存模板方法改成失效清除的策略: 1. 检查缓存中是否存在 2. 若存在,则检查是否需要热加载 3. 若需要热加载,则对比文件是否已更改 4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步 5. 若不存在,重新解析模板并将模板压入缓存 6. 返回模板 |
|
返回顶楼 | |
发表时间:2007-11-23
jindw 的策略不错.呵呵
|
|
返回顶楼 | |
发表时间:2007-11-24
Quake Wang 写道 A. 内存缓存及持久化缓存不应该作为2种缓存,缓存在哪里应该由Cache的实现者处理,而不是Cache的使用者就提前设计好的,比如Ehcache,OSCache都提供了基于内存和文件系统持久化的缓存。 这个肯定,引擎只会留出一个Cache的SPI,不会决定什么内存缓存和持久化缓存, 上面说的只是现在内置的Cache实现:持久化缓存包装内存缓存(Decorator方式),他们都实现了Cahce接口,只是组成了链,可以被替换掉。 Quake Wang 写道 4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步 5. 若不存在,重新解析模板并将模板压入缓存 第5步会不会出现并发修改异常? |
|
返回顶楼 | |
发表时间:2007-11-24
javatar 写道 Quake Wang 写道 4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步 5. 若不存在,重新解析模板并将模板压入缓存 第5步会不会出现并发修改异常? 这依赖于Cache的实现,成熟的Cache解决方案都会在put方法内部解决并发问题。 |
|
返回顶楼 | |