我正在尝试利用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已经是这个代码库的依赖项,非常方便。
答案 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
慢。