缓存主要作用是提高应用程序吞吐量和响应性,当然也有负面影响,占用更多内存。在设计SqlTemplate也有个简单的本地缓存,sql模板实际只需要解释一次就可以了,以后的调用复用之前解释过。开始的时候是使用简单的HashMap实现的,但是在并发情况下会出现重复解释,下面是第一版的代码片段。
public class Configuration {
private ConcurrentHashMap<String, SqlTemplate > templateCache;
......
public SqlTemplate getTemplate(final String content) {
if (cacheTemplate) { //是 否则缓存模板
SqlTemplate sqlTemplate = templateCache.get(content);
if (sqlTemplate != null){
sqlTemplate = createTemplate(content) ;
templateCache.put(content, sqlTemplate) ;
}
return sqlTemplate;
}
return createTemplate(content);
}
//解释构建模板对象
private SqlTemplate createTemplate(String content) {
SqlTemplate template = new SqlTemplate.SqlTemplateBuilder(this, content)
.build();
return template;
}
......
}
通过看上面的代码,很容易发现问题的所在,一个线进createTemplate解释模板,其他线程不知道这个模板已经在解释中,所以可能会出现同sql模板会出现多次计算。理想的情况下其他线程知道模板在解释中,只需等待完成。下面是使用FutureTask改进后的第二版本代码,看似很完美^_^。
public class Configuration {
private ConcurrentHashMap<String, FutureTask<SqlTemplate>> templateCache;
......
public SqlTemplate getTemplate(final String content) {
if (cacheTemplate) {////是 否则缓存模板
FutureTask<SqlTemplate> f = templateCache.get(content);
if (f == null) {
FutureTask<SqlTemplate> ft = new FutureTask<SqlTemplate>(
new Callable<SqlTemplate>() {
public SqlTemplate call() throws Exception {
return createTemplate(content);
}
});
f = templateCache.putIfAbsent(content, ft);
if (f == null) {
ft.run();
f = ft;
}
}
try {
return f.get();
} catch (Exception e) {
templateCache.remove(content); //防止缓存污染
throw new RuntimeException(e);
}
}
return createTemplate(content);
}
//解释构建模板对象
private SqlTemplate createTemplate(String content) {
SqlTemplate template = new SqlTemplate.SqlTemplateBuilder(this, content)
.build();
return template;
}
......
}
第二版解决了第一版的缺陷(重复解释),并发性也不错,能很好返回已经解释过的结果。这次缓存不是结果值,而是一个FutureTask,关于Future详细使用请参考啊了个里写的《Java 并发之 Future 接口》。关于SqlTemplate请参考http://my.oschina.net/u/866190/blog/175800
来源:oschina
链接:https://my.oschina.net/u/866190/blog/177021