7

如何以有效的方式防止多次同时加载缓存中不存在的值?

典型的缓存用法是以下伪代码:

Object get(Object key) {
 Object value = cache.get(key);
 if (value == null) {
  value = loadFromService(key);
  cache.set(key,value);
 }
 return value;
}

问题:在从服务(数据库、WebService、RemoteEJB 或其他任何东西)加载值之前,可能会同时进行第二次调用,这将使值再次加载。

比如我在缓存用户X的所有item,这个用户经常被查看,并且有很多item,很有可能同时调用他所有item的load,导致服务器负载过重。

我可以使get功能同步,但这会迫使其他搜索等待,没有多大意义。我可以为每个键创建新锁,但我不知道在Java中管理如此大量的锁是否是个好主意(这部分是特定于语言的,我将其标记为的原因java)。

还是我可以使用另一种方法?如果是这样,什么是最有效的?

4

3 回答 3

7

您通常可以做的事情是使用 Object 的 hashCode。

您可以拥有一组基于 hashCode 使用的锁,以减少冲突的机会。或者作为一种技巧,您可以使用自动装箱字节始终返回相同对象的事实。

Object get(Object key) {
    Object value = cache.get(key);
    if (value == null) {
        // every possible Byte is cached by the JLS.
        Byte b = Byte.valueOf((byte) key.hashCode());
        synchronized (b) {
            value = cache.get(key);
            if (value == null) {
                value = loadFromService(key);
                cache.set(key, value);
            }
        }
    }
    return value;
}
于 2013-01-23T10:15:53.310 回答
3

不要重新发明轮子,使用番石榴LoadingCache记忆供应商

如果您使用的是 Ehcache,请阅读read-through,这是您要求的模式。您必须实现该CacheEntryFactory接口以指示缓存如何在缓存未命中时读取对象,并且您必须Ehcache使用SelfPopulatingCache.

于 2013-01-23T10:24:54.527 回答
1

在加载时,在地图中插入一个中间对象而不是结果,以指示加载开始但尚未完成。下面的 java.util.concurrent.FutureTask 用于中间对象:

Object get(final Object key) throws Exception {
    boolean doRun = false;
    Object value;
    synchronized (cache) {
        value = cache.get(key);
        if (value == null) {
            value = new FutureTask(new Callable() {
                @Override
                public Object call() throws Exception {
                    Object loadedValue = loadFromService(key);
                    synchronized (cache) {cache.put(key, loadedValue);};
                    return loadedValue;
                }

            });
            cache.put(key, value);
            doRun=true;
        }
    }
    if (value instanceof FutureTask) {
        FutureTask task = (FutureTask) value;
        if (doRun) {
            task.run();
        }
        return task.get();
    }
    return value;
}`
于 2013-01-23T11:16:44.900 回答