- 浏览: 1701124 次
- 性别:
- 来自: 杭州699号
文章分类
最新评论
-
莫莫摸:
为什么不用dubbo
RCP数据传输模型回顾 -
大胡子爸爸:
String, Class 都实现了Serializable接 ...
RPC框架几行代码就够了 -
lss598018587:
谢谢大神分享,比起新手看复杂的dubbo框架还不如看大神的这一 ...
RPC框架几行代码就够了 -
15606915740:
你好,请问一下。<dubbo:consumer filt ...
Dubbo文档 -
joqk12345:
...
一些设计上的基本常识
简述一下CommonTemplate(http://www.commontemplate.org)的模板工厂每次获取模板的过程如下:
检查内存缓存中是否存在,
若不存在,则检查持久化缓存中是否存在,
若还不存在,则重新解析模板并将模板压入内存缓存及持久化缓存,
若存在,则检查是否需要热加载,
若需要热加载,则对比文件是否已更改,
若已更改,则重新解析模板并将模板压入内存缓存及持久化缓存,
返回模板
因为很多检测点,开始整个过程都被同步执行了,
和huangyh讨论时,
他提出,如果去掉同步,会出现什么?
总结是:
一、在并发下,同一模板会多次被解析和缓存,
二、非同步缓存容器保存时可能出错,
讨论后发现:
第一种影响对程序的正确性没什么影响的,只是“觉得”多次解析和缓存会影响性能,
而实际上,这些影响性能比同步块对引擎活性的影响要小很多,
即然如此,那只要保证缓存容器自身是线程安全的,就没必要同步整个获取过程。
缓存容器采用的是策略模式,
引擎留出的SPI是:org.commontemplate.config.Cache接口,重构后:
第一种方式:
在Cache接口的文档中声明,Cache的实现者都必须线程安全,即自行实现同步。
第二种方式:
引擎在调用Cache之前,使用装饰器模式统一外包装实现同步。
第三种方式:
结合上面两种方式,再加一个是否需要外部同步的标识性接口,引擎基于此判断是否需要外部同步。
嗯,这是个问题,看来预读时要避开IO读写等耗时操作才行。
如果没有热加载问题,进行预读是很好的想法,如:
另外,IO操作出现在第10行,而不是第9行,因为:
上面的load是回调TemplateSourceLoader接口的loadTemplateSource(),
TemplateSourceLoader.loadTemplateSource通常不是真的去加载数据,而是简单的创建表示信息,
以FileTemplateSourceLoader为例,当调用loadTemplateSource时,只是将new File()包装成TemplateSource返回,并没有打开流,应该不会有大的性能消耗。
当调用TemplateSource的getReader()或getLastModified()才有可能打开流或读取内容,
也就是第10行:templateSourceComparator.isModified(template, templateSource)) 可能出现大的性能消耗,isModified可能比的是最后修改时间,也可能是全文对比,要看TemplateSourceComparator接口的具体实现,但不管怎么样,即然要拿到文件的实际信息,此时总会有IO操作。
思路挺好,代码有问题。
syn..(globalLock)
syn..(resourceLock ){
之间未同步。不用同步块的形式
恩,只是我想:两个块之间还需要同步吗?
第一个同步块确保取到资源对应的唯一锁。
第二个同步块确保同一的资源不产生多个实例。
再第二个同步块里还有一次读取操作,我还没想到需要在两块之间同步的理由。
不用,是我搞错了:)
思路挺好,代码有问题。
syn..(globalLock)
syn..(resourceLock ){
之间未同步。不用同步块的形式
恩,只是我想:两个块之间还需要同步吗?
第一个同步块确保取到资源对应的唯一锁。
第二个同步块确保同一的资源不产生多个实例。
再第二个同步块里还有一次读取操作,我还没想到需要在两块之间同步的理由。
4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存
第5步会不会出现并发修改异常?
这依赖于Cache的实现,成熟的Cache解决方案都会在put方法内部解决并发问题。
正解。内存缓存,用一个同步的HashMap就可以了,譬如继承LinkedHashMap,重写方法removeEldestEntry便可以实现一个LRU策略的Map,够成熟。
4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存
第5步会不会出现并发修改异常?
这依赖于Cache的实现,成熟的Cache解决方案都会在put方法内部解决并发问题。
A. 内存缓存及持久化缓存不应该作为2种缓存,缓存在哪里应该由Cache的实现者处理,而不是Cache的使用者就提前设计好的,比如Ehcache,OSCache都提供了基于内存和文件系统持久化的缓存。
这个肯定,引擎只会留出一个Cache的SPI,不会决定什么内存缓存和持久化缓存,
上面说的只是现在内置的Cache实现:持久化缓存包装内存缓存(Decorator方式),他们都实现了Cahce接口,只是组成了链,可以被替换掉。
4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存
第5步会不会出现并发修改异常?
因为lock集合也必须有缓存策略,不能无限增大
leadyu
现在的想法已经更加完善了
不过,lock集合我认为就不需要缓存策略了,区区一个简单对象,由他去把。
因为lock集合也必须有缓存策略,不能无限增大
这样如何?
检查内存缓存中是否存在,
若不存在,则检查持久化缓存中是否存在,
若还不存在,则重新解析模板并将模板压入内存缓存及持久化缓存,
若存在,则检查是否需要热加载,
若需要热加载,则对比文件是否已更改,
若已更改,则重新解析模板并将模板压入内存缓存及持久化缓存,
返回模板
因为很多检测点,开始整个过程都被同步执行了,
和huangyh讨论时,
他提出,如果去掉同步,会出现什么?
总结是:
一、在并发下,同一模板会多次被解析和缓存,
二、非同步缓存容器保存时可能出错,
讨论后发现:
第一种影响对程序的正确性没什么影响的,只是“觉得”多次解析和缓存会影响性能,
而实际上,这些影响性能比同步块对引擎活性的影响要小很多,
即然如此,那只要保证缓存容器自身是线程安全的,就没必要同步整个获取过程。
缓存容器采用的是策略模式,
引擎留出的SPI是:org.commontemplate.config.Cache接口,重构后:
第一种方式:
在Cache接口的文档中声明,Cache的实现者都必须线程安全,即自行实现同步。
第二种方式:
引擎在调用Cache之前,使用装饰器模式统一外包装实现同步。
第三种方式:
结合上面两种方式,再加一个是否需要外部同步的标识性接口,引擎基于此判断是否需要外部同步。
评论
20 楼
javatar
2007-11-29
Java并发实践上有一个案例,和这里的缓存有几分相似,
有一个很耗时的运算需要缓存其计算结果,采用备忘录模式实现,如:
备忘录缓存方案一:
这里备忘录的整个compute过程被同步,相当于我最原始的低效方案,
备忘录缓存方案二:
将线程安全性委给cache,注:这个方案的cache用了ConcurrentHashMap,它是线程安全的。相当于huangyh提出的全部不同步。
备忘录缓存方案三:
他给出的第三种方案和leadyu的差不多,使用Future(相当上一帖的Entry)封装,因为这里资源获取过程不固定(通用方案),所以使用了Callable进行回调资源获取过程(求值),
这个方案的 if (f == null) 不安全,特殊性可能会进行多次运算,下面的方案四解决这个问题。
备忘录缓存方案四(最终方案):
有一个很耗时的运算需要缓存其计算结果,采用备忘录模式实现,如:
public interface Computable<A, V> { V compute(A arg) throws InterruptedException; } public class ExpensiveFunction implements Computable<String, BigInteger> { public BigInteger compute(String arg) { // 假设这里非常耗时... return new BigInteger(arg); } }
备忘录缓存方案一:
public class Memoizer1<A, V> implements Computable<A, V> { @GuardedBy("this") private final Map<A, V> cache = new HashMap<A, V>(); private final Computable<A, V> c; public Memoizer1(Computable<A, V> c) { this.c = c; } public synchronized V compute(A arg) throws InterruptedException { V result = cache.get(arg); if (result == null) { result = c.compute(arg); cache.put(arg, result); } return result; } }
这里备忘录的整个compute过程被同步,相当于我最原始的低效方案,
备忘录缓存方案二:
public class Memoizer2<A, V> implements Computable<A, V> { private final Map<A, V> cache = new ConcurrentHashMap<A, V>(); private final Computable<A, V> c; public Memoizer2(Computable<A, V> c) { this.c = c; } public V compute(A arg) throws InterruptedException { V result = cache.get(arg); if (result == null) { result = c.compute(arg); cache.put(arg, result); } return result; } }
将线程安全性委给cache,注:这个方案的cache用了ConcurrentHashMap,它是线程安全的。相当于huangyh提出的全部不同步。
备忘录缓存方案三:
public class Memoizer3<A, V> implements Computable<A, V> { private final Map<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>(); private final Computable<A, V> c; public Memoizer3(Computable<A, V> c) { this.c = c; } public V compute(final A arg) throws InterruptedException { Future<V> f = cache.get(arg); if (f == null) { Callable<V> eval = new Callable<V>() { public V call() throws InterruptedException { return c.compute(arg); } }; FutureTask<V> ft = new FutureTask<V>(eval); f = ft; cache.put(arg, ft); ft.run(); // call to c.compute happens here } try { return f.get(); } catch (ExecutionException e) { throw launderThrowable(e.getCause()); } } }
他给出的第三种方案和leadyu的差不多,使用Future(相当上一帖的Entry)封装,因为这里资源获取过程不固定(通用方案),所以使用了Callable进行回调资源获取过程(求值),
这个方案的 if (f == null) 不安全,特殊性可能会进行多次运算,下面的方案四解决这个问题。
备忘录缓存方案四(最终方案):
public class Memoizer<A, V> implements Computable<A, V> { private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>(); private final Computable<A, V> c; public Memoizer(Computable<A, V> c) { this.c = c; } public V compute(final A arg) throws InterruptedException { while (true) { Future<V> f = cache.get(arg); if (f == null) { Callable<V> eval = new Callable<V>() { public V call() throws InterruptedException { return c.compute(arg); } }; FutureTask<V> ft = new FutureTask<V>(eval); f = cache.putIfAbsent(arg, ft); if (f == null) { f = ft; ft.run(); } } try { return f.get(); } catch (CancellationException e) { cache.remove(arg, f); } catch (ExecutionException e) { throw launderThrowable(e.getCause()); } } } }
19 楼
javatar
2007-11-28
再重构:
private final ReloadController reloadController; private final TemplateSourceComparator templateSourceComparator; private final Cache cache; private static final class Entry { Entry() {} private Object resource = null; Object get() { return resource; } void set(Object template) { this.resource = template; } } private Template cache(String name, String encoding) throws IOException, ParseException { Entry entry = null; // 先进行一次无锁读取,此过程不调用任何有副作用(修改状态)的函数,也不调用任何耗时操作 if (reloadController == null || ! reloadController.shouldReload(name)) { // 不需要热加载时才进行 entry = (Entry)cache.get(name); if (entry != null) { Template template = (Template)entry.get(); if (template != null) return template; } } // 缓存总锁,锁定各条目的获取 synchronized(cache){ entry = (Entry)cache.get(name); // 这里必需重新读取 if(entry == null){ entry = new Entry(); cache.put(name, entry); } assert(entry != null); // 后验条件 } // 单条目锁,锁定资源获取过程 synchronized (entry) { Template template = (Template)entry.get(); if (template == null) { // 不存在,解析加载 TemplateSource templateSource = load(name, encoding); template = parse(templateSource); entry.set(template); } else { // 已存在,检查热加载 if (reloadController != null && reloadController.shouldReload(name)) { // 是否需要检查热加载 TemplateSource templateSource = load(name, encoding); if (templateSourceComparator != null && templateSourceComparator.isModified(template, templateSource)) { // 比较是否已更改 template = parse(templateSource); entry.set(template); return template; } } } assert(template != null); // 后验条件 return template; } }
18 楼
leadyu
2007-11-27
上面第9第10行可以去掉,多余了。
我暂时收回这句话,呵呵,下午翻了翻,Tomcat5 的代码,只要option的getDeployment开关打开,也就是在开发模式下,他每次servlet的访问都去编译java文件,编译class文件,new新的ClassLoader,加载新的servlet。汗。。。神阿,你不要这样,怎么你也这么搞?搞得我以为你有什么绝招,翻了半天你的代码。----to Tomcat
如果不在开发模式下,JSP他不更新。但是有一点他应该是优化了,他记录下来了资源的路径,直接加载,省去了查找了开销,应该来说查找的开销会比读文件多数倍,特别是项目文件多的时候。我觉得,CommonTemplate有时间的话也可以这么做,在TemplateLock里面记录path信息,应该不叫TemplateLock,应该改成
ResorceEntry。
不知道weblogic的ChangeWareClassLoader是不是也是这样?
引用
而且上面还有一个问题,就是热加载。每次访问都去load一下是不行的,load是一个秒级操作,对于系统级的组件来说(比如common template or JSP)这是绝对不能接受的。
我暂时收回这句话,呵呵,下午翻了翻,Tomcat5 的代码,只要option的getDeployment开关打开,也就是在开发模式下,他每次servlet的访问都去编译java文件,编译class文件,new新的ClassLoader,加载新的servlet。汗。。。神阿,你不要这样,怎么你也这么搞?搞得我以为你有什么绝招,翻了半天你的代码。----to Tomcat
如果不在开发模式下,JSP他不更新。但是有一点他应该是优化了,他记录下来了资源的路径,直接加载,省去了查找了开销,应该来说查找的开销会比读文件多数倍,特别是项目文件多的时候。我觉得,CommonTemplate有时间的话也可以这么做,在TemplateLock里面记录path信息,应该不叫TemplateLock,应该改成
ResorceEntry。
不知道weblogic的ChangeWareClassLoader是不是也是这样?
17 楼
javatar
2007-11-27
leadyu 写道
2)保证需要加载资源时,其他访问线程可以先等着,不要也去加载,浪费CPU和IO操作,load是非常慢的一个操作。
嗯,这是个问题,看来预读时要避开IO读写等耗时操作才行。
如果没有热加载问题,进行预读是很好的想法,如:
... TemplateLock templateLock = (TemplateLock)templateCache.get(name); if (templateLock != null && templateLock.getTemplate() != null) { return templateLock.getTemplate() } ...
另外,IO操作出现在第10行,而不是第9行,因为:
上面的load是回调TemplateSourceLoader接口的loadTemplateSource(),
TemplateSourceLoader.loadTemplateSource通常不是真的去加载数据,而是简单的创建表示信息,
以FileTemplateSourceLoader为例,当调用loadTemplateSource时,只是将new File()包装成TemplateSource返回,并没有打开流,应该不会有大的性能消耗。
当调用TemplateSource的getReader()或getLastModified()才有可能打开流或读取内容,
也就是第10行:templateSourceComparator.isModified(template, templateSource)) 可能出现大的性能消耗,isModified可能比的是最后修改时间,也可能是全文对比,要看TemplateSourceComparator接口的具体实现,但不管怎么样,即然要拿到文件的实际信息,此时总会有IO操作。
16 楼
leadyu
2007-11-27
这样不行,锁得的就是Load方法,还放在外面无锁预读,不是白锁了吗?(见上第9行),虽然说,两个线程同时Load没问题,只要Put的时候同步一下get就可以了,但是,大并发下,多个线程,同时Load一个资源,做同样的比较,不是太浪费了吗?这样锁得目的,两个:1)是保证线程安全,因为无法知道parse语法分析时会不会涉及一些共享资源
2)保证需要加载资源时,其他访问线程可以先等着,不要也去加载,浪费CPU和IO操作,load是非常慢的一个操作。
而且上面还有一个问题,就是热加载。每次访问都去load一下是不行的,load是一个秒级操作,对于系统级的组件来说(比如common template or JSP)这是绝对不能接受的。
2)保证需要加载资源时,其他访问线程可以先等着,不要也去加载,浪费CPU和IO操作,load是非常慢的一个操作。
而且上面还有一个问题,就是热加载。每次访问都去load一下是不行的,load是一个秒级操作,对于系统级的组件来说(比如common template or JSP)这是绝对不能接受的。
15 楼
javatar
2007-11-27
是否应像jindw那样加入无锁预读?那就需要两次热加载检查,如:
还有没有更好的方式?
如果没问题,准备下一个版本加入。
private Template cache(String name, String encoding) throws IOException, ParseException { // 先进行一次无锁读取,此过程不调用任何有副作用(修改状态)的函数 TemplateLock templateLock = (TemplateLock)templateCache.get(name); if (templateLock != null && templateLock.getTemplate() != null) { Template template = templateLock.getTemplate(); if (reloadController != null && reloadController.shouldReload(name)) { // 是否需要检查热加载 TemplateSource templateSource = load(name, encoding); if (templateSourceComparator != null && templateSourceComparator.isModified(template, templateSource)) { // 比较是否已更改 template = null; } } if (template != null) return template; } // 工厂锁,锁定模板锁 synchronized(factoryLock) { TemplateLock templateLock = (TemplateLock)templateCache.get(name); // 此处应保持重新读取 // 同上帖... } // 模板锁,锁定模板获取过程 synchronized (templateLock) { // 同上帖... } }
还有没有更好的方式?
如果没问题,准备下一个版本加入。
14 楼
javatar
2007-11-25
发布了新版本0.7.3:http://www.commontemplate.org
采用了leadyu的方式,使用外包装分级锁。
org.commontemplate.engine.TemplateFactoryImpl 的相关代码:
采用了leadyu的方式,使用外包装分级锁。
org.commontemplate.engine.TemplateFactoryImpl 的相关代码:
private final Cache templateCache; // 策略传入 private final ReloadController reloadController; // 策略传入 private final TemplateSourceComparator templateSourceComparator; // 策略传入 private final Object factoryLock = new Object(); private static final class TemplateLock { TemplateLock() {} private Template template = null; Template getTemplate() { return template; } void setTemplate(Template template) { this.template = template; } } private Template cache(String name, String encoding) throws IOException, ParseException { TemplateLock templateLock = null; // 工厂锁,锁定模板锁 synchronized(factoryLock) { templateLock = (TemplateLock)templateCache.get(name); if(templateLock == null){ templateLock = new TemplateLock(); templateCache.put(name, templateLock); } assert(templateLock != null); // 后验条件 } // 模板锁,锁定模板获取过程 synchronized (templateLock) { Template template = templateLock.getTemplate(); if (template == null) { // 不存在,解析加载 TemplateSource templateSource = load(name, encoding); template = parse(templateSource); templateLock.setTemplate(template); } else { // 已存在,检查热加载 if (reloadController != null && reloadController.shouldReload(name)) { // 是否需要检查热加载 TemplateSource templateSource = load(name, encoding); if (templateSourceComparator != null && templateSourceComparator.isModified(template, templateSource)) { // 比较是否已更改 template = parse(templateSource); templateLock.setTemplate(template); return template; } } } assert(template != null); // 后验条件 return template; } }
13 楼
nihongye
2007-11-24
jindw 写道
nihongye 写道
思路挺好,代码有问题。
syn..(globalLock)
syn..(resourceLock ){
之间未同步。不用同步块的形式
恩,只是我想:两个块之间还需要同步吗?
第一个同步块确保取到资源对应的唯一锁。
第二个同步块确保同一的资源不产生多个实例。
再第二个同步块里还有一次读取操作,我还没想到需要在两块之间同步的理由。
不用,是我搞错了:)
12 楼
jindw
2007-11-24
nihongye 写道
思路挺好,代码有问题。
syn..(globalLock)
syn..(resourceLock ){
之间未同步。不用同步块的形式
恩,只是我想:两个块之间还需要同步吗?
第一个同步块确保取到资源对应的唯一锁。
第二个同步块确保同一的资源不产生多个实例。
再第二个同步块里还有一次读取操作,我还没想到需要在两块之间同步的理由。
11 楼
nihongye
2007-11-24
Quake Wang 写道
javatar 写道
Quake Wang 写道
4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存
第5步会不会出现并发修改异常?
这依赖于Cache的实现,成熟的Cache解决方案都会在put方法内部解决并发问题。
正解。内存缓存,用一个同步的HashMap就可以了,譬如继承LinkedHashMap,重写方法removeEldestEntry便可以实现一个LRU策略的Map,够成熟。
10 楼
nihongye
2007-11-24
我想错了
9 楼
QuakeWang
2007-11-24
javatar 写道
Quake Wang 写道
4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存
第5步会不会出现并发修改异常?
这依赖于Cache的实现,成熟的Cache解决方案都会在put方法内部解决并发问题。
8 楼
javatar
2007-11-24
Quake Wang 写道
A. 内存缓存及持久化缓存不应该作为2种缓存,缓存在哪里应该由Cache的实现者处理,而不是Cache的使用者就提前设计好的,比如Ehcache,OSCache都提供了基于内存和文件系统持久化的缓存。
这个肯定,引擎只会留出一个Cache的SPI,不会决定什么内存缓存和持久化缓存,
上面说的只是现在内置的Cache实现:持久化缓存包装内存缓存(Decorator方式),他们都实现了Cahce接口,只是组成了链,可以被替换掉。
Quake Wang 写道
4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存
第5步会不会出现并发修改异常?
7 楼
huangyh
2007-11-23
jindw 的策略不错.呵呵
6 楼
QuakeWang
2007-11-23
有2点想法:
A. 内存缓存及持久化缓存不应该作为2种缓存,缓存在哪里应该由Cache的实现者处理,而不是Cache的使用者就提前设计好的,比如Ehcache,OSCache都提供了基于内存和文件系统持久化的缓存。
B. 作为模板语言的解析结果缓存,可以不加同步,我觉得这一点要求也不是必须的:
在Cache接口的文档中声明,Cache的实现者都必须线程安全,即自行实现同步。
比如我用非线程安全的HashMap来实现一个简单的内存缓存,获取缓存模板方法改成失效清除的策略:
1. 检查缓存中是否存在
2. 若存在,则检查是否需要热加载
3. 若需要热加载,则对比文件是否已更改
4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存
6. 返回模板
A. 内存缓存及持久化缓存不应该作为2种缓存,缓存在哪里应该由Cache的实现者处理,而不是Cache的使用者就提前设计好的,比如Ehcache,OSCache都提供了基于内存和文件系统持久化的缓存。
B. 作为模板语言的解析结果缓存,可以不加同步,我觉得这一点要求也不是必须的:
javatar 写道
在Cache接口的文档中声明,Cache的实现者都必须线程安全,即自行实现同步。
比如我用非线程安全的HashMap来实现一个简单的内存缓存,获取缓存模板方法改成失效清除的策略:
1. 检查缓存中是否存在
2. 若存在,则检查是否需要热加载
3. 若需要热加载,则对比文件是否已更改
4. 若已更改,清除缓存中对于的这个Key,并且跳到第一步
5. 若不存在,重新解析模板并将模板压入缓存
6. 返回模板
5 楼
jindw
2007-11-23
leadyu 写道
javatar 写道
楼上两位好想法!
。。。。
用Resource包装,主要为了保证entity和lock的缓存策略一致性,避免,一个存在一个不存在。
。。。。
因为lock集合也必须有缓存策略,不能无限增大
leadyu
现在的想法已经更加完善了
不过,lock集合我认为就不需要缓存策略了,区区一个简单对象,由他去把。
4 楼
leadyu
2007-11-23
javatar 写道
楼上两位好想法!
按单实例分别开锁,可以减少很大同步面,且缓存容器可以不同步。
leadyu将template包装成Resource,再拿Resource作为锁
jindw将锁放在一个集合内,用同一个key却取锁,与template完全分离
哪一种会更有优势?
那是否可以直接用template(被缓存对象)本身作为锁?
用Resource包装,主要为了保证entity和lock的缓存策略一致性,避免,一个存在一个不存在。
按单实例分别开锁,可以减少很大同步面,且缓存容器可以不同步。
leadyu将template包装成Resource,再拿Resource作为锁
jindw将锁放在一个集合内,用同一个key却取锁,与template完全分离
哪一种会更有优势?
那是否可以直接用template(被缓存对象)本身作为锁?
因为lock集合也必须有缓存策略,不能无限增大
3 楼
javatar
2007-11-23
楼上两位好想法!
按单实例分别开锁,可以减少很大同步面,且缓存容器可以不同步。
leadyu将template包装成Resource,再拿Resource作为锁
jindw将锁放在一个集合内,用同一个key却取锁,与template完全分离
哪一种会更有优势?
那是否可以直接用template(被缓存对象)本身作为锁?
看来是不行,template在锁之前不存在。
按单实例分别开锁,可以减少很大同步面,且缓存容器可以不同步。
leadyu将template包装成Resource,再拿Resource作为锁
jindw将锁放在一个集合内,用同一个key却取锁,与template完全分离
哪一种会更有优势?
那是否可以直接用template(被缓存对象)本身作为锁?
看来是不行,template在锁之前不存在。
2 楼
jindw
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; }
这样如何?
1 楼
leadyu
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); }
这样应该是安全的,大家再看看有没更好的写法
发表评论
-
以HTTL为例讲讲模块分包&领域模型&扩展框架
2011-10-09 20:08 16755注:该博客内容已加入 ... -
CommonTemplate增加HTML标签版语法外套
2008-09-09 10:33 2974CommonTemplate(http://www.commo ... -
CommonTemplate访问者设计思考
2008-09-03 10:45 1782经过多个版本的调整, CommonTemplate(http: ... -
CommonTemplate发布0.8.6版本
2008-08-26 20:49 1803CommonTemplate发布0.8.6版本 ... -
CommonTemplate发布0.8.5版本
2008-08-04 13:23 1897CommonTemplate发布0.8.5版本(2008-08 ... -
CommonTemplate加入代码生成器
2008-07-21 13:15 2247模板引擎经常被用于做代码生成, 为此, CommonTempl ... -
加入对YAML数据格式的支持
2008-07-01 12:41 4005CommonTemplate(http://www.commo ... -
嵌套注释语法思考
2008-06-29 14:40 4023主流的C/C++/Java/C#等语言,都将注释语法设计成不可 ... -
转:开源协议
2008-06-10 17:23 2233来源:网络 (1)Contrib ... -
CommonTemplate完成查看器Viewer.exe(及安装程序)
2008-06-04 15:12 1875完成查看器初始版本. 实现功能: 双击*.ctl文件, 自动读 ... -
CommonTemplate完成外部构建树或表达式接口
2008-05-31 11:01 1951CommonTemplate: http://www.comm ... -
CommonTemplate异常国际化完成
2008-05-26 11:48 1929周未把一个累活给干了, 就是异常信息的国际化. 总共有220多 ... -
CommonTemplate加入对无穷数的支持.
2008-05-23 11:07 2742用"*"号表示无穷数, 常在下标号中使用, ... -
CommonTemplate导出模板所需变量结构
2008-05-12 18:28 2262在velocity的邮件列表中收到下面的邮件: Simon G ... -
CommonTemplate完成$snatch指令
2008-05-06 09:20 1905CommonTemplate(http://www.commo ... -
关于CTE当前API无法支持从非引擎方式构建模板树
2008-04-28 17:20 1797因隐藏了模板树的实现, 现在CommonTemplate(ht ... -
CommonTemplate完成DEBUG单步调试
2008-04-21 09:56 2534CommonTemplate(http://www.commo ... -
CommonTemplate准备加入$breakpoint指令
2008-04-19 10:30 2211准备在CommonTemplate( http://www.c ... -
很高兴桂林兄加入CommonTemplate的开发
2008-04-05 20:49 2934桂林的blog: http://jasongreen.itey ... -
展开式序列实现
2008-03-31 22:47 2102现在CommonTemplate(http://www.com ...
相关推荐
本文探讨了基于现场可编程门阵列(FPGA)技术,结合DDR3高速动态随机访问存储器(Dynamic Random Access Memory)来实现高速图像缓存策略的研究。DDR3以其高带宽和低功耗特点,成为高速数据缓存的理想选择。在设计中...
例如,synchronized用于保证共享资源在同一时刻仅被一个线程访问,而volatile则保证了变量的可见性和有序性,避免缓存不一致问题。 在高并发场景下,设计良好的并发策略至关重要。例如,可以使用线程池...
《Oracle远程数据库同步方法的研究》这篇论文主要探讨了在分布式环境下如何实现Oracle数据库的同步策略,以及解决数据同步系统中的关键问题。以下是论文的核心内容: 首先,文章提到了分布式数据库的特点,即在逻辑...
Coherence支持多种缓存类型,包括本地缓存、近端缓存(Near Cache)和分区缓存,用户可以根据应用场景选择合适的缓存策略。 在实际使用中,Coherence通常与各种企业级应用框架和数据库协同工作,例如与Hibernate的...
6. **内存管理**:在内存有限的情况下,立方体重构可能涉及如何有效地缓存数据,以平衡内存使用和查询性能。 7. **并行处理和分布式计算**:随着数据量的增大,立方体的重构可能需要利用多核处理器或分布式计算集群...
- **依赖于缓存技术**:为缓解读取压力,通常需要借助缓存技术,这就要求对现有系统进行一定程度的重构。 - **可能限制扩展能力**:随着业务的增长,单个主库的性能瓶颈将成为制约因素。 #### 方案三:使用数据库...
2. **请求缓存**:对于重复的网络请求,可以采用缓存策略,将已经获取的数据存储起来,避免不必要的网络传输。这不仅节省了网络带宽,也减少了处理请求所需的CPU时间,间接降低了能耗。 3. **异步处理**:网络游戏...
此外,需要处理session同步、数据缓存同步和文件系统共享等问题。这一步涉及负载均衡技术、分布式系统原理和一致性解决方案。 第六步,分库。当单一数据库无法应对高并发时,数据库分库成为必要。这涉及到数据库...
3. **缓存策略**:利用内存或分布式缓存提高数据访问速度,减轻数据库压力。 4. **数据库优化**:包括读写分离、分库分表、索引优化等,确保数据的高效存储和检索。 5. **CDN(Content Delivery Network)**:通过全球...
- 设计高效的缓存机制,减轻数据库压力。 - 实现购物车同步功能,确保多终端一致性体验。 - 支持多种支付方式集成,提高用户购买转化率。 3. **结算页架构与设计**: - 提供清晰简洁的操作界面,优化用户体验。...
分布式缓存是分布式存储中的一个重要组成部分,它的出现是为了解决分布式系统中的数据共享和同步问题。分布式缓存能够提高数据访问速度,并且能够缓存热点数据,减少对后端存储系统的访问压力。 在分布式存储系统中...
- **缓存一致性**:理解缓存与数据库之间数据同步的策略。 - **分布式缓存**:研究如何在多个服务器间共享缓存数据。 #### 架构演变第五步:增加Web服务器 **背景**:随着访问量持续增长,单一Web服务器无法满足高...
本指导学习将深入探讨如何利用这两种技术来重构网上书店,提升用户体验,实现数据的无缝同步。 Ajax是一种在不刷新整个网页的情况下,能够更新部分网页内容的技术。它通过JavaScript发送异步HTTP请求到服务器,获取...
- **配置缓存大小和策略**: 根据应用程序的需求来调整缓存的大小和替换策略,以达到最佳性能。 - **采用高级缓存预取技术**: 通过预测数据访问模式,在实际需要之前预先加载数据到缓存中,减少访问延迟。 - **非...
缓存会引入数据同步的挑战,尤其是在分布式系统中,需要根据业务需求选择合适的同步策略。例如,使用链表缓存来优化帖子列表是一种常见的做法,同时,工具如Memcached也被广泛使用。此外,MySQL的Heap引擎提供了将...
5. **性能提升**:通过压缩和最小化CSS和JavaScript文件,减小图片大小,利用缓存策略等方法,可以提升页面加载速度,从而提升用户体验。 6. **部署流程**:重构完成后,了解不同的部署选项(如使用Git进行版本控制...
在设计缓存机制时,我们遇到了数据一致性问题,通过引入CAP理论和BASE原则,以及采用事件驱动的同步策略,成功解决了这个问题,提高了系统的整体性能。 【软件可靠性评价】 在我负责的医疗软件项目中,软件可靠性...
在实际应用中,需要根据具体场景选择合适的缓存级别和策略。 #### 性能调优 为了进一步提升Hibernate的性能,可以采取以下措施: - **查询优化**:合理利用索引、分页查询等技术来提高查询效率。 - **批处理**:...
本文主要探讨为何要遵循特定的重构策略以及它们带来的益处。 首先,对于CSS,我们应该遵循以下原则: 1. **样式表置于顶部**:浏览器在解析HTML时,遇到`<link>`或`<style>`标签会暂停渲染,直到样式表加载完成,...
理解缓存层次结构(L1、L2、L3缓存)和缓存替换策略(如LRU、LFU)可以帮助优化代码,使其更有效地利用缓存,减少内存访问时间。 7. **编译器优化**: 编译器在生成机器代码时,可以进行各种优化,如循环展开、 ...