緩存主要做用是提升應用程序吞吐量和響應性,固然也有負面影響,佔用更多內存。在設計SqlTemplate也有個簡單的本地緩存,sql模板實際只須要解釋一次就能夠了,之後的調用複用以前解釋過。開始的時候是使用簡單的HashMap實現的,可是在併發狀況下會出現重複解釋,下面是初版的代碼片斷。 java
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改進後的第二版本代碼,看似很完美^_^。 sql
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 緩存