2012-03-15 21 views
1

私はLRUキャッシュを作成しようとしている次のコードを持っています。私はキャッシュのランダムな容量に対して実行しているランナークラスを持っています。ただし、キャッシュのサイズが容量を超えています。 FixLRUメソッドを同期させると、キャッシュ・サイズが100を超えるとより正確になりますが、それは遅くなります。 synchronizedキーワードを削除すると、キャッシュの精度が低下します。同期がうまくいきません

このアイデアを正しく作成する方法はありますか?より正確な?

import java.util.concurrent.ConcurrentHashMap; 

public abstract class Cache<TKey, TValue> implements ICache<TKey,TValue>{ 

    private final ConcurrentHashMap<TKey,TValue> _cache; 

    protected Cache() 
    { 
     _cache= new ConcurrentHashMap<TKey, TValue>(); 
    } 

    protected Cache(int capacity){ 
     _cache = new ConcurrentHashMap<TKey, TValue>(capacity); 
    } 

    @Override 
    public void Put(TKey key, TValue value) { 
     _cache.put(key, value); 
    } 

    @Override 
    public TValue Get(TKey key) { 
     TValue value = _cache.get(key); 

     return value; 
    } 

    @Override 
    public void Delete(TKey key) { 
     _cache.remove(key); 
    } 

    @Override 
    public void Purge() { 
     for(TKey key : _cache.keySet()){ 
      _cache.remove(key); 
     } 
    } 

    public void IterateCache(){ 

     for(TKey key: _cache.keySet()){ 
      System.out.println("key:"+key+" , value:"+_cache.get(key)); 
     } 

    } 

    public int Count() 
    { 
     return _cache.size(); 
    } 


} 


import java.util.concurrent.ConcurrentLinkedQueue; 

public class LRUCache<TKey,TValue> extends Cache<TKey,TValue> implements ICache<TKey, TValue> { 

    private ConcurrentLinkedQueue<TKey> _queue; 
    private int capacity; 
    public LRUCache(){ 
     _queue = new ConcurrentLinkedQueue<TKey>(); 
    } 

    public LRUCache(int capacity){ 
     this(); 
     this.capacity = capacity; 
    } 

    public void Put(TKey key, TValue value) 
    { 
     FixLRU(key); 

     super.Put(key, value); 
    } 

    private void FixLRU(TKey key) 
    { 
     if(_queue.contains(key)) 
     { 
      _queue.remove(key); 
      super.Delete(key); 
     } 

     _queue.offer(key); 

     while(_queue.size() > capacity){ 
      TKey keytoRemove =_queue.poll(); 
      super.Delete(keytoRemove); 
     } 
    } 

    public TValue Get(TKey key){ 

     TValue _value = super.Get(key); 

     if(_value == null){ 
      return null; 
     } 

     FixLRU(key); 

     return _value; 
    } 

    public void Delete(TKey key){ 

     super.Delete(key); 
    } 

} 

public class RunningLRU extends Thread{ 

    static LRUCache<String, String> cache = new LRUCache<String, String>(50); 

    public static void main(String [ ] args) throws InterruptedException{ 

     Thread t1 = new RunningLRU(); 
     t1.start(); 
     Thread t2 = new RunningLRU(); 
     t2.start(); 
     Thread t3 = new RunningLRU(); 
     t3.start(); 
     Thread t4 = new RunningLRU(); 
     t4.start(); 
     try { 
      t1.join(); 
      t2.join(); 
      t3.join(); 
      t4.join(); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     System.out.println(cache.toString()); 
     cache.IterateCache(); 
     System.out.println(cache.Count()); 

    } 

    @Override 
    public void run() { 
     for(int i=0;i<100000;i++) 
      cache.Put("test"+i, "test"+i); 
    } 

} 
+0

FixLRUメソッドのメインブロックを中心にsynchronized(capacity){// code}を試してみてください。 – darijan

答えて

2

エントリを追加した後、追加のエントリをクリーンアップします。これにより、キャッシュが必要以上に大きくなる時間が最小限に抑えられます。また、クリーンアップを実行するためにsize()をトリガーすることもできます。

どのようにすれば正しく動作させることができますか?

あなたのアプリケーションは、アプリケーションの動作を反映していますか?あなたがそれをハンマーで押さえていないとき、キャッシュは適切に(またはもっと近く)動作するかもしれません。 ;)

このテストにアプリケーションの動作が反映されている場合は、LRUCacheが最適な選択ではない可能性があります。

+0

それは私がそれをハムマーリングしていないとき、うまく動作します。つまり、シングルスレッドです。私が同期を使用したとき。メソッドは、バッキングキューを修正するには、それもうまく動作しますが、その後、私は同期を削除すると、より遅くなります。キャッシュのサイズがその容量を超えています。 – DarthVader

+0

'put()'の最後にクリーンアップすると、どれくらい悪いですか?あなたの能力が恣意的である可能性があることを考えると、どれくらいの問題がありますか? –

+0

それはあなたが推測するようにランダムです。私はそれが正確であることを望むだけです。私はコンカレントコレクションを使用していますが、まだこれらの問題があります。私が並行コレクションを使用していて同期キーワードを使用している場合は、ポイントは何ですか?右。 – DarthVader

2

あなたの問題は、特別なのputメソッドputIfAbsent()の同期バージョンを使用していないことが原因と思われます。あなたがそれを使用しない場合、はではなくのように振る舞います - 通常のマップ、例えばHashMapのように。

あなたがそれを使用する場合は、あなたがあなたのPut()方法が正しい署名を持っていないので、並行性をサポートするために、(それがTValueを返す必要があります)、値を返さのみ使用し続けなければなりません。インタフェースを再設計する必要があります。

また、Javaの土地では、.Netの土地とは異なり、われわれのメソッドには、小文字の先頭にたとえばput()という名前が付けられており、Put()ではありません。したがって、メソッドの名前を変更することをお勧めします。

関連する問題