如何使用Guava LoadingCache更新存储在缓存中的链接列表值

时间:2014-12-04 00:21:20

标签: java multithreading caching linked-list guava

我正在尝试利用Guava库中的LoadingCache来缓存LinkedList

LoadingCache<Integer, LinkedList<String>> cache;

我已经设置CacheLoader来处理未命中,这很正常。但是,还有另一个系统需要提交现有缓存条目的更新。每次更新都需要附加到LinkedList,并且会以相当快的速度(每分钟数千次)到达。最后,它需要是线程安全的。

这是一个简单的方法,说明了逻辑,但不是线程安全的:

public void add(Integer key, String value) {
    LinkedList<String> list = cache.get(key);
    list.add(value);
    cache.put(key, list);
}

有关如何使这项工作的任何建议?我可以查看其他库,但是Guava 14已经是这个代码库的依赖项,非常方便。

1 个答案:

答案 0 :(得分:3)

中的最后一行
public void add(Integer key, String value) {
    LinkedList<String> list = cache.get(key);
    list.add(value);
    cache.put(key, list);
}
由于您已经修改了从缓存中获取的对象,因此不需要

。也许你需要的只是

public void add(Integer key, String value) {
    LinkedList<String> list = cache.get(key);
    synchronized (list) {
        list.add(value);
    }
}

这取决于驱逐的发生。如果根本没有驱逐,那么它将起作用。如果在更新方法完成之前某个条目可能被驱逐,那么你就不幸了。

尽管如此,还有一个简单的解决方案:使用全局锁定可以正常工作,但显然效率低下。所以使用锁列表:

private static final CONCURRENCY_LEVEL = 64; // must be power of two
List<Object> locks = Lists.newArrayList(); // an array would do as well
for (int i=0; i<CONCURRENCY_LEVEL; ++i) locks.add(new Object());

public void add(Integer key, String value) {
    synchronized (locks.get(hash(key))) {
        cache.get(key).add(value);
    }
}

其中hash - 取决于您的密钥的分布 - 可以像key.intValue() & (CONCURRENCY_LEVEL-1)一样简单,也可以像here那样随机化分发。


虽然上面的锁定列表应该有效,但是在Guava中有Striped.lock(int),这使得它更简单并且可以处理填充(请参阅false sharing了解它的内容好的,等等。


很可能你不应该使用LinkedList,因为它几乎总是比ArrayList慢。