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

时间:2010-08-23 20:52:46

标签: java multithreading servlets caching concurrency

我正在尝试正确设置服务器端缓存,我正在寻找对我目前设置的建设性批评。当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;
}

缓存由Servlet设置如下:

private ProductManager productManager;

public void init(ServletConfig config) throws ServletException {
    productManager = ProductManager.getInstance();
}

最后,这就是我访问它的方式:

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;
}

4 个答案:

答案 0 :(得分:1)

我想到的一些事情:

  • 将缓存的ProductData实例存储在地图中,以便您可以在固定时间内查找缓存的实例,而不必搜索列表。
  • lookup方法不是线程安全的。
  • 只有lookup()实际上会从数据库中加载值:您是否希望其他lookup方法也加载缓存(如果尚未加载)以加快单{{}的检索速度1}}实例?

答案 1 :(得分:1)

你正在艰难地做这件事。单瓶风格的图案是完全没必要的。只需实现一个ServletContextListener即可在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;
    }

}

您在web.xml注册的内容如下:

<listener>
    <listener-class>com.example.Config</listener-class>
</listener>

这样你可以在任何servlet中获取它,如下所示:

Config config = Config.getInstance(getServletContext());
Map<Long, Product> products = config.getProducts();
// ...

答案 2 :(得分:0)

我建议你把缓存作为一个hashmap:

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();

您可以将缓存设置为ProductData类中的静态字段,以便减少耦合和移动部件。

使用id查找的hashmap将基本上是恒定的时间,而当forData的数量随着时间的推移而增加时,for循环的搜索将会增加。

答案 3 :(得分:0)

请查看Guava的Suppliers.memoizeWithExpiration()和MapMaker。这两个(在你的情况下我认为MapMaker更相关)将让你在几分钟内创建一个缓存函数。

让自己头疼,使用API​​:)