Web Java アプリケーションで重いオブジェクトの単純なキャッシュを実装したいと考えています。しかし、私はそれを適切に行う方法を理解できません。
何か足りないのでしょうか、それとも ConcurrentHashMap メソッド (putIfAbsent など) では不十分であり、追加の同期が必要ですか?
これを行うためのより簡単な API (メモリ ストレージ内、外部構成なし) はありますか?
P.
Web Java アプリケーションで重いオブジェクトの単純なキャッシュを実装したいと考えています。しかし、私はそれを適切に行う方法を理解できません。
何か足りないのでしょうか、それとも ConcurrentHashMap メソッド (putIfAbsent など) では不十分であり、追加の同期が必要ですか?
これを行うためのより簡単な API (メモリ ストレージ内、外部構成なし) はありますか?
P.
ケンの答えに加えて、後で破棄される重いオブジェクトを作成することが受け入れられない場合 (何らかの理由で、キーごとに 1 つのオブジェクトのみが作成されることを保証したい場合)、次の方法でこれを行うことができます....実際には、しないでください。自分でやらないでください。google-collections (現在はguava ) MapMaker クラスを使用します。
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 を参照してください)。
キャッシュしようとしているものに対して一時的に複数のインスタンスを作成しても安全な場合は、次のような「ロックフリー」キャッシュを実行できます。
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;
}
複数のスレッドが「競合」してキーのアイテムを作成および追加できますが、「勝つ」のは1つだけです。
「重いオブジェクト」をキャッシュに入れる代わりに、軽いファクトリ オブジェクトを使用してアクティブなキャッシュを作成できます。
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;
ConcurrentHashMap は、必要に応じて十分なはずです putIfAbsent はスレッドセーフです。
どれだけ簡単にできるかわからない
ConcurrentMap myCache = new ConcurrentHashMap();
ポール