我想在Web java应用程序中实现重量级对象的简单缓存。但我无法弄清楚如何正确地做到这一点。
我是否遗漏了某些内容或者ConcurrentHashMap方法(putIfAbsent,...)还不够,需要进行额外的同步?
是否有更好的简单API(在内存中,没有外部配置)来执行此操作?
P上。
答案 0 :(得分:25)
除了Ken的答案之外,如果创建一个后来被扔掉的重量级对象是不可接受的(你想保证每个键只创建一个对象,由于某种原因),那么你可以通过...实际上,不要。不要自己动手。使用google-collections(现在guava)MapMaker class:
Map<KeyType, HeavyData> cache = new MapMaker<KeyType, HeavyData>()
.makeComputingMap(new Function<KeyType, HeavyData>() {
public HeavyData apply(KeyType key) {
return new HeavyData(key); // Guaranteed to be called ONCE for each key
}
});
然后一个简单的cache.get(key)
只是工作并完全消除你不必担心并发和同步化的棘手问题。
请注意,如果您想添加一些更高级的功能,例如到期,那只是
Map<....> cache = new MapMaker<....>()
.expiration(30, TimeUnit.MINUTES)
.makeComputingMap(.....)
如果需要,您还可以轻松地为键或数据使用软值或弱值(有关详细信息,请参阅Javadoc)
答案 1 :(得分:14)
如果暂时为您尝试缓存的事物安装多个实例是安全的,您可以像这样执行“无锁”缓存:
public Heavy instance(Object key) {
Heavy info = infoMap.get(key);
if ( info == null ) {
// It's OK to construct a Heavy that ends up not being used
info = new Heavy(key);
Heavy putByOtherThreadJustNow = infoMap.putIfAbsent(key, info);
if ( putByOtherThreadJustNow != null ) {
// Some other thread "won"
info = putByOtherThreadJustNow;
}
else {
// This thread was the winner
}
}
return info;
}
多个线程可以“竞争”为密钥创建和添加项目,但只有一个应该“赢”。
答案 2 :(得分:2)
您可以使用轻型工厂对象来创建活动缓存,而不是将“重物”放入缓存中。
public abstract class LazyFactory implements Serializable {
private Object _heavyObject;
public getObject() {
if (_heavyObject != null) return _heavyObject;
synchronized {
if (_heavyObject == null) _heavyObject = create();
}
return _heavyObject;
}
protected synchronized abstract Object create();
}
// here's some sample code
// create the factory, ignore negligible overhead for object creation
LazyFactory factory = new LazyFactory() {
protected Object create() {
// do heavy init here
return new DbConnection();
};
};
LazyFactory prev = map.pufIfAbsent("db", factory);
// use previous factory if available
return prev != null ? prev.getObject() : factory.getObject;
答案 3 :(得分:0)
ConcurrentHashMap应该足以满足您的需求putIfAbsent是线程安全的。
不确定你能得到多少简单
ConcurrentMap myCache = new ConcurrentHashMap();
保
答案 4 :(得分:0)
我意识到这是一个旧帖子,但在java 8中,这可以在不使用ConcurrentHashMap创建可能未使用的重型对象的情况下完成。
public class ConcurrentCache4<K,V> {
public static class HeavyObject
{
}
private ConcurrentHashMap<String, HeavyObject> cache = new ConcurrentHashMap<>();
public HeavyObject get(String key)
{
HeavyObject heavyObject = cache.get(key);
if (heavyObject != null) {
return heavyObject;
}
return cache.computeIfAbsent(key, k -> new HeavyObject());
}
}