Java 正确设置简单的服务器端缓存
我正在尝试正确地设置服务器端缓存,我正在寻找对当前设置的建设性批评。缓存在Servlet启动时加载,并且不再更改,因此实际上它是一个只读缓存。显然,它需要在Servlet的整个生命周期中都留在内存中。这是我如何设置的Java 正确设置简单的服务器端缓存,java,multithreading,servlets,caching,concurrency,Java,Multithreading,Servlets,Caching,Concurrency,我正在尝试正确地设置服务器端缓存,我正在寻找对当前设置的建设性批评。缓存在Servlet启动时加载,并且不再更改,因此实际上它是一个只读缓存。显然,它需要在Servlet的整个生命周期中都留在内存中。这是我如何设置的 private static List<ProductData> _cache; private static ProductManager productManager; private ProductManager() { try { lo
private static List<ProductData> _cache;
private static ProductManager productManager;
private ProductManager() {
try {
lookup();
} catch (Exception ex) {
_cache = null;
}
}
public synchronized static ProductManager getInstance() {
if (productManager== null) {
productManager= new ProductManager();
}
return productManager;
}
最后,这是我访问它的方式:
public static ProductData lookup(long id) throws Exception {
if (_cache != null) {
for (int i = 0; i < _cache.size(); i++) {
if (_cache.get(i).id == id) {
return _cache.get(i);
}
}
}
// Look it up in the DB.
}
public static List<ProductData> lookup() throws Exception {
if (_cache != null) {
return _cache;
}
// Read it from the DB.
_cache = list;
return list;
}
公共静态ProductData查找(长id)引发异常{
如果(_cache!=null){
对于(int i=0;i<_cache.size();i++){
if(_cache.get(i).id==id){
返回_cache.get(i);
}
}
}
//在数据库中查找它。
}
公共静态列表查找()引发异常{
如果(_cache!=null){
返回缓存;
}
//从数据库中读取它。
_缓存=列表;
退货清单;
}
我建议您将缓存设置为哈希映射:
private static HashMap<Long,ProductData> _cache = new HashMap<Long,ProductData>();
// lookup by id becomes
return _cache.get(id);
// lookup of the complete collection of ProductData :
return _cache.values();
private static HashMap_cache=new HashMap();
//按id查找变为
返回_cache.get(id);
//查找ProductData的完整集合:
返回_cache.values();
可以将缓存设置为ProductData类中的静态字段,以减少耦合和移动部件
使用hashmap时,按id查找的时间基本上是恒定的,而使用for循环的搜索将随着ProductData数量的增加而增加。想到的几件事:
- 将缓存的
实例存储在映射中,这样您就可以在固定时间内查找缓存的实例,而无需搜索列表ProductData
方法不是线程安全的lookup
- 只有
将实际从数据库加载值:您不希望另一个lookup()
方法也加载缓存(如果尚未加载)以加快单个lookup
实例的检索吗ProductData
省得你头疼,使用API:)你做得很艰难。一个单一风格的模式是完全没有必要的。只需在webapp启动(和关闭)上实现一个钩子,这样您就可以在webapp启动期间在应用程序范围中加载和存储数据
public class Config implements ServletContextListener {
private static final String ATTRIBUTE_NAME = "com.example.Config";
private Map<Long, Product> products;
@Override
public void contextInitialized(ServletContextEvent event) {
ServletContext context = event.getServletContext();
context.setAttribute(ATTRIBUTE_NAME, this);
String dbname = context.getInitParameter("dbname");
products = Database.getInstance(dbname).getProductDAO().map();
}
@Override
public void contextDestroyed(ServletContextEvent event) {
// NOOP.
}
public static Config getInstance(ServletContext context) {
return (Config) context.getAttribute(ATTRIBUTE_NAME);
}
public Map<Long, Product> getProducts() {
return products;
}
}
通过这种方式,您可以在任何servlet中获得它,如下所示:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
Config config = Config.getInstance(getServletContext());
Map<Long, Product> products = config.getProducts();
// ...
Config=Config.getInstance(getServletContext());
Map products=config.getProducts();
// ...
具体来说,我应该如何处理线程安全问题?同步块和/或并发集合类?并发集合对您没有帮助,因为我相信您只加载了一次缓存,在那之后它不会改变。第二个lookup
方法(实际加载缓存)需要同步。否则,两个线程都可能发现\u cache
为null
,并且都可能尝试从数据库加载。
Config config = Config.getInstance(getServletContext());
Map<Long, Product> products = config.getProducts();
// ...