2012-02-19 6 views
3

現在キャッシュを実装中です。私は以下のような基本的な実装を完了しました。私がしたいのは、特定の条件を満たすエントリを削除するスレッドを実行することです。リスト内のいくつかの要素を定期的にクリーンアップするバックグラウンドスレッドを実行するにはどうすればよいですか?

class Cache { 
    int timeLimit = 10; //how long each entry needs to be kept after accessed(marked) 
    int maxEntries = 10; //maximum number of Entries 
    HashSet<String> set = new HashSet<String>(); 
    public void add(Entry t){ 
     .... 
    } 

    public Entry access(String key){ 
     //mark Entry that it has been used 
     //Since it has been marked, background thread should remove this entry after timeLimit seconds. 
     return set.get(key); 
    } 
    .... 
} 

私の質問は、スレッドがセット内のエントリの周りに行くとmarked && (last access time - now)>timeLimitされているものを削除するように、私は、バックグラウンドスレッドを実装する方法、ありますか?

編集

上記はちょうど私が同期文を書いていないことを、コードのバージョンを簡略化されています。

+2

「キャッシュ」はスレッドセーフではありません。複数のスレッドからアクセスしたい場合は、まずそのスレッドで作業してください。 –

+0

代わりに 'add'メソッドからクリーンアップを呼び出すと、余分なスレッドは必要ありません。それがあなたのためのオプションであるかどうかは、パフォーマンス要件に依存します。 –

+0

ConcurrentHashMap –

答えて

8

なぜあなたは車輪を再発明していますか? EhCache(とまともなキャッシュ実装)がこれを行います。また、はるかに軽量MapMakerCacheGuavaは、古いエントリを自動的に削除することができます。

実際にを実装したい場合は、それほど単純ではありません。

  1. 覚えておいてください。エントリを保存するには、ConcurrentHashMapまたは​​キーワードを使用する必要があります。これは本当に難しいかもしれません。

  2. 何とか各エントリの何らかの形で最後のアクセス時間を保存する必要があります。エントリにアクセスするたびに、そのタイムスタンプを更新する必要があります。

  3. 退去ポリシーについて考えてみましょう。あなたのキャッシュにmaxEntries以上がある場合は、最初に削除するものがありますか?

  4. 本当にバックグラウンドスレッドが必要ですか?

    これは驚くべきことですが、EhCache(エンタープライズ・レディ・アンド・エキスパート)はバックグラウンド・スレッドを使用して古いエントリを無効にしません)。代わりに、マップがいっぱいになるまで待機し、エントリを遅延して削除します。スレッドは高価であるため、これは良いトレードオフのように見えます。

  5. バックグラウンドスレッドを使用している場合は、キャッシュごとに1つ、またはグローバルに1つ存在する必要がありますか?新しいキャッシュを作成している間に新しいスレッドを開始するか、すべてのキャッシュのグローバルリストを保持しますか?これは、あなたがこれらすべての質問に回答したら、実装は非常に簡単です

...あなたが思うよりも難しいです:毎秒ほど、あなたはすでに書いた条件が満たされた場合、すべてのエントリを通過し、エントリを削除します。

+2

あなたがスレッドを持っていることは言うまでもありませんが、それは強力な参照を保持します(弱い参照が眠ったときに唯一の参照が保持されていることを確認しない限り) –

+1

'MapMaker'はかなり古くなっています。新しいキャッシュを好む。 –

+0

@LouisWasserman:ありがとう、私はGuavaを実際に使っていない。私は自分の答えを更新しました。 –

0

まず、あなたのコレクションへのアクセスを行うのいずれか​​またはConcurrentHashSet下のコメントで示されているようSetをベースConcurrentHashMapを使用しています。

第2に、新しいスレッドを作成し、それを以前のコレクションを定期的に反復して要素を削除するエンドレスループとして実装します。このクラスは、コンストラクタで正しいコレクションで初期化されるように記述する必要があります。そのため、「適切なコレクションにアクセスするにはどうすればよいですか」を気にする必要はありません。

+0

'ConcurrentHashSet'?参照してください:http://stackoverflow.com/questions/6992608 –

+0

それは 'Collections.newSetFromMap(新しいConcurrentHashMap ())' –

0

これは、GuavaCacheタイプを個人的に使用します。これはすでにスレッドセーフで、いくつかの時間制限に基づいてキャッシュからの削除のためのメソッドが組み込まれています。

new Thread(new Runnable() { 
     public void run() { 
      cache.cleanUp(); 
      try { Thread.sleep(MY_SLEEP_DURATION); } catch (Exception e) {}; 
     } 
    }).start(); 
+0

スレッドは1回だけ実行され、停止しますか?または、キャッシュにアクセスしてリソースが不足するたびに起動する必要がありますか? 0_o –

0

私はあなたが本当にバックグラウンドスレッドを必要と想像しません:あなたは、スレッドが定期的に掃引したい場合は、ちょうどこのような何かを行うことができます。代わりに、ルックアップを実行する前または後に期限切れのエントリを削除するだけで済みます。これにより、実装全体が簡素化され、その違いを伝えることが非常に難しくなります。ところで

:にはget(key)がないので、あなたがのLinkedHashMapを使用する場合は、removeEldestEntryをオーバーライドすることにより、LRUキャッシュとして使用することができます(たとえば、そのjavadocを参照)、すべての

+0

ええ、私は 'removeEldestEntry'が非常に実現可能であることを知りました。しかし、私はちょうど質問の最後の文で説明されている2つの条件を満たすエントリを削除しようとした。しかし、情報をありがとう! – user482594

+0

チェックを追加するには、get()もオーバーライドします。 –

0

まず、ご提示のコードは不完全ですHashSet(なので、あなたはMapの何らかの種類を意味すると思います)、コードには「マーキング」はありません。キャッシュを行う方法は数多くあり、キャッシュしようとしているものとその理由を知らなくても、最良のソリューションを選ぶことは困難です。

キャッシュを実装する場合、通常、データ構造に複数のスレッドが同時にアクセスすることが想定されています。最初に行う必要があることは、スレッドセーフなバッキングデータ構造を使用することです。 HashMapはスレッドセーフではありませんが、ConcurrentHashMapはスレッドセーフです。また、Guava,Javolutionおよびhigh-scale libという多数の他の並行した実装があります(GuavaJavolutionおよびhigh-scale lib)。マップの他にキャッシュを構築する他の方法があり、その有用性はユースケースによって異なります。それにもかかわらず、バックグラウンドスレッドを必要とせず、期限切れのオブジェクトをキャッシュから取得しようとしたときに退去させる場合でも、バッキングデータ構造をスレッドセーフにする必要があります。または、GCにSoftReferenceを使用してエントリを削除させることもできます。

キャッシュの内部をスレッドセーフにしたら、キャッシュを定期的にスイープ/反復して古いエントリを削除する新しい(最も可能性の高い)スレッドを起動することができます。スレッドはループでこれを行います(中断するまで繰り返します)。その後、各スイープの後に一定時間スリープします。

ただし、独自のキャッシュ実装を構築する価値があるかどうかを検討する必要があります。スレッドセーフなコードを書くのは簡単ではないので、独自のキャッシュ実装を書く前に勉強することをお勧めします。 Java Concurrency in Practiceという本をお勧めします。

これを簡単に実行できる方法は、もちろん、既存のキャッシュ実装を使用することです。 Javaの土地には、独自のトレードオフを持つ多くのオプションがあります。

  • EhCacheJCSは、ほとんどのキャッシングが1は、典型的な「企業」のアプリケーションで見つけるだろう必要が合う両方の汎用キャッシュです。
  • Infinispanは、分散使用に最適化されたキャッシュであるため、1台のマシンに収まるものより多くのデータをキャッシュできます。私はまたそのConcurrentMapベースのAPIが好きです。
  • 他にも触れられているように、Googles GuavaライブラリにはCache APIがあります。このAPIは、小さなメモリ内のキャッシュには非常に便利です。

キャッシュ内のエントリ数を制限したいので、キャッシュの代わりにオブジェクトプールが必要な場合があります。

  • Apache Commons-Poolは、あなた自身で構築しようとしているものに似たAPIを持っています。一方で、かなり異なるAPIを持っています。私はそれを書いたので、ほとんど言及していません。おそらくあなたが望むものではありませんが、あなたがキャッシュしようとしているものとその理由を知らずに誰が確信することができますか?
関連する問題