データのLRUキャッシュとして、いつでも最大500エントリを許可するLinkedHashMap
とともにtrueを使用しました。accessOrder
しかし、スケーラビリティの問題があるため、スレッドセーフな代替手段に移りたいと思います。ConcurrentHashMap
その点では良いようですが、の機能が欠けており、にaccessOrder
ありremoveEldestEntry(Map.Entry e)
ますLinkedHashMap
。誰かがいくつかのリンクを指すか、実装を容易にするのを手伝ってくれますか?
6 に答える
私は最近ConcurrentHashMap<String,CacheEntry>
、CacheEntryが実際のアイテムをラップし、キャッシュエビクション統計を追加するという同様のことを行いました:有効期限、挿入時間(FIFO / LIFOエビクションの場合)、最後に使用された時間(LRU / MRUエビクションの場合)、ヒット数(LFU /の場合) MFU eviction)など。実際のevictionは同期さArrayList<CacheEntry>
れ、eviction戦略に適切なComparatorを使用してCollections.sort()を作成および実行します。これは費用がかかるため、各エビクションはCacheEntriesの下位5%を失います。ただし、パフォーマンスの調整が役立つと確信しています。
あなたの場合、FIFOを実行しているので、別のConcurrentLinkedQueueを保持できます。ConcurrentHashMapにオブジェクトを追加するときは、そのオブジェクトのConcurrentLinkedQueue.add()を実行します。エントリを削除する場合は、ConcurrentLinkedQueue.poll()を実行して最も古いオブジェクトを削除してから、ConcurrentHashMapからも削除します。
更新:この領域の他の可能性には、Javaコレクション同期ラッパーとJava1.6ConcurrentSkipListMapが含まれます。
ehcache のような多くのキャッシュ ソリューションのいずれかを使用してみましたか? ReadWriteLock で LinkedHashMap を使用してみてください。これにより、同時読み取りアクセスが可能になります。
これは今では古いように思えるかもしれませんが、少なくとも私自身の履歴追跡のために、ここに私の解決策を追加します: K->WeakReference のサブクラス、ConcurrentLinkedQueue、および値オブジェクトの逆シリアル化を定義するインターフェイスをマップする ConcurrentHashMap を組み合わせましたK に基づいて、LRU キャッシングを正しく実行します。キューは強力な参照を保持し、GC は必要に応じてメモリから値を削除します。キュー サイズの追跡には AtomicInteger が関係していました。これは、キューを実際に調べて、いつ追い出すかを判断することができないためです。キャッシュは、マップ管理だけでなく、キューからの削除/キューへの追加も処理します。GC がメモリから値を削除した場合、逆シリアル化インターフェイスの実装が値の取得を処理します。また、ディスクへのスプール/スプールされたものの再読み取りを含む別の実装もありました。
「スレッドセーフ」な代替手段でスケーラビリティの問題を解決したいとおっしゃっています。ここでの「スレッド セーフ」とは、構造が同時アクセスの試行に対して耐性があることを意味し、外部同期なしで同時使用によって破損することはありません。ただし、そのような許容範囲は、必ずしも「スケーラビリティ」の向上に役立つとは限りません。最も単純な (通常は誤った方向に導かれる) アプローチでは、構造を内部的に同期させようとしますが、非アトミックな check-then-act操作は安全ではありません。
LRU キャッシュでは、構造全体を少なくともある程度認識する必要があります。削除するタイミングを決定するには、メンバーの数やメンバーのサイズなどの情報が必要です。次に、要素の読み取り、追加、または削除を同時に試行しながら、削除を調整できる必要があります。「メイン」構造への同時アクセスに必要な同期を削減しようとすると、エビクション メカニズムと戦い、エビクション ポリシーの保証の精度が低くなります。
現在受け入れられている回答では、「エントリを削除したいとき」に言及しています。そこに摩擦があります。エントリを削除するタイミングはどのようにしてわかりますか? この決定を下すために一時停止する必要がある他の操作はどれですか?
マップを でラップしますCollections.synchronizedMap()
。追加のメソッドを呼び出す必要がある場合synchronize
は、この呼び出しから返されたマップで、元のマップで元のメソッドを呼び出します (例については、javadoc を参照してください)。キーなどを反復処理する場合も同じことが当てはまります。