Java 正确设置简单的服务器端缓存

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

我正在尝试正确地设置服务器端缓存,我正在寻找对当前设置的建设性批评。缓存在Servlet启动时加载,并且不再更改,因此实际上它是一个只读缓存。显然,它需要在Servlet的整个生命周期中都留在内存中。这是我如何设置的

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
    实例的检索吗

请查看番石榴的供应商。请记住Expatition()和MapMaker。这两种方法(在您的例子中,我认为MapMaker更相关)都可以让您在几分钟内创建一个缓存函数


省得你头疼,使用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();
// ...