Index: src/java/org/apache/solr/common/util/ConcurrentLRUCache.java
===================================================================
--- src/java/org/apache/solr/common/util/ConcurrentLRUCache.java	(revision 714218)
+++ src/java/org/apache/solr/common/util/ConcurrentLRUCache.java	(working copy)
@@ -22,9 +22,9 @@
  * @version $Id$
  * @since solr 1.4
  */
-public class ConcurrentLRUCache {
+public class ConcurrentLRUCache<K,V> {
 
-  private final ConcurrentHashMap<Object, CacheEntry> map;
+  private final ConcurrentHashMap<Object, CacheEntry<K,V>> map;
   private final int upperWaterMark, lowerWaterMark;
   private volatile boolean stop = false;
   private final ReentrantLock markAndSweepLock = new ReentrantLock(true);
@@ -34,24 +34,34 @@
   private final Stats stats = new Stats();
   private final int acceptableWaterMark;
   private long oldestEntry = 0;  // not volatile, only accessed in the cleaning method
+  private final EvictionListener<K,V> evictionListener;
+  private final Object monitor = new Object();
+  private final boolean dedicatedCleanupThread ;
 
-  public ConcurrentLRUCache(int upperWaterMark, final int lowerWaterMark, int acceptableWatermark, int initialSize, boolean runCleanupThread, boolean runNewThreadForCleanup, final int delay) {
+  public ConcurrentLRUCache(int upperWaterMark, final int lowerWaterMark, int acceptableWatermark,
+                            int initialSize, boolean runCleanupThread, boolean runNewThreadForCleanup,
+                            EvictionListener<K,V> evictionListener) {
     if (upperWaterMark < 1) throw new IllegalArgumentException("upperWaterMark must be > 0");
     if (lowerWaterMark >= upperWaterMark)
       throw new IllegalArgumentException("lowerWaterMark must be  < upperWaterMark");
-    map = new ConcurrentHashMap<Object, CacheEntry>(initialSize);
+    map = new ConcurrentHashMap<Object, CacheEntry<K,V>>(initialSize);
     newThreadForCleanup = runNewThreadForCleanup;
     this.upperWaterMark = upperWaterMark;
     this.lowerWaterMark = lowerWaterMark;
     this.acceptableWaterMark = acceptableWatermark;
+    this.evictionListener = evictionListener;
+    dedicatedCleanupThread =  runCleanupThread;
     if (runCleanupThread) {
       new Thread() {
         public void run() {
           while (true) {
             if (stop) break;
-            try {
-              Thread.sleep(delay * 1000);
-            } catch (InterruptedException e) {/*no op*/ }
+            synchronized(monitor){
+              try {
+                monitor.wait();
+              } catch (InterruptedException e) { }
+            }
+            if (stop) break;
             markAndSweep();
           }
         }
@@ -63,8 +73,8 @@
     islive = live;
   }
 
-  public Object get(Object key) {
-    CacheEntry e = map.get(key);
+  public V get(K key) {
+    CacheEntry<K,V> e = map.get(key);
     if (e == null) {
       if (islive) stats.missCounter.incrementAndGet();
       return null;
@@ -73,10 +83,11 @@
     return e.value;
   }
 
-  public Object remove(Object key) {
-    CacheEntry cacheEntry = map.remove(key);
+  public V remove(K key) {
+    CacheEntry<K,V> cacheEntry = map.remove(key);
     if (cacheEntry != null) {
       stats.size.decrementAndGet();
+      if(evictionListener != null) evictionListener.evictedEntry(cacheEntry.key , cacheEntry.value);
       return cacheEntry.value;
     }
     return null;
@@ -112,7 +123,13 @@
             markAndSweep();
           }
         }.start();
-      } else {
+      }
+      if(dedicatedCleanupThread){
+        synchronized(monitor){
+          monitor.notify();
+        }
+      }
+      if(!newThreadForCleanup && ! dedicatedCleanupThread) {
         markAndSweep();
       }
     }
@@ -157,13 +174,13 @@
       int wantToKeep = lowerWaterMark;
       int wantToRemove = sz - lowerWaterMark;
 
-      CacheEntry[] eset = new CacheEntry[sz];
+      CacheEntry<K,V>[] eset = new CacheEntry[sz];
       int eSize = 0;
 
       // System.out.println("newestEntry="+newestEntry + " oldestEntry="+oldestEntry);
       // System.out.println("items removed:" + numRemoved + " numKept=" + numKept + " esetSz="+ eSize + " sz-numRemoved=" + (sz-numRemoved));
 
-      for (CacheEntry ce : map.values()) {
+      for (CacheEntry<K,V> ce : map.values()) {
         // set lastAccessedCopy to avoid more volatile reads
         ce.lastAccessedCopy = ce.lastAccessed;
         long thisEntry = ce.lastAccessedCopy;
@@ -209,7 +226,7 @@
 
         // iterate backward to make it easy to remove items.
         for (int i=eSize-1; i>=0; i--) {
-          CacheEntry ce = eset[i];
+          CacheEntry<K,V> ce = eset[i];
           long thisEntry = ce.lastAccessedCopy;
 
           if (thisEntry > newestEntry - wantToKeep) {
@@ -258,7 +275,7 @@
         PQueue queue = new PQueue(wantToRemove);
 
         for (int i=eSize-1; i>=0; i--) {
-          CacheEntry ce = eset[i];
+          CacheEntry<K,V> ce = eset[i];
           long thisEntry = ce.lastAccessedCopy;
 
           if (thisEntry > newestEntry - wantToKeep) {
@@ -308,7 +325,7 @@
         // avoid using pop() since order doesn't matter anymore
         for (Object o : queue.getValues()) {
           if (o==null) continue;
-          CacheEntry ce = (CacheEntry)o;
+          CacheEntry<K,V> ce = (CacheEntry<K,V>)o;
           evictEntry(ce.key);
           numRemoved++;
         }
@@ -355,22 +372,23 @@
   }
 
 
-  private void evictEntry(Object key) {
-    Object o = map.remove(key);
+  private void evictEntry(K key) {
+    CacheEntry<K,V> o = map.remove(key);
     if (o == null) return;
     stats.size.decrementAndGet();
     stats.evictionCounter++;
+    if(evictionListener != null) evictionListener.evictedEntry(o.key,o.value);
   }
 
 
   public Map getLatestAccessedItems(long n) {
     // we need to grab the lock since we are changing lastAccessedCopy
     markAndSweepLock.lock();
-    Map result = new LinkedHashMap();
-    TreeSet<CacheEntry> tree = new TreeSet<CacheEntry>();
+    Map<K,V> result = new LinkedHashMap<K,V>();
+    TreeSet<CacheEntry<K,V>> tree = new TreeSet<CacheEntry<K,V>>();
     try {
-      for (Map.Entry<Object, CacheEntry> entry : map.entrySet()) {
-        CacheEntry ce = entry.getValue();
+      for (Map.Entry<Object, CacheEntry<K,V>> entry : map.entrySet()) {
+        CacheEntry<K,V> ce = entry.getValue();
         ce.lastAccessedCopy = ce.lastAccessed;
         if (tree.size() < n) {
           tree.add(ce);
@@ -384,7 +402,7 @@
     } finally {
       markAndSweepLock.unlock();
     }
-    for (CacheEntry e : tree) {
+    for (CacheEntry<K,V> e : tree) {
       result.put(e.key, e.value);
     }
     return result;
@@ -398,17 +416,18 @@
     map.clear();
   }
 
-  public Map<Object, CacheEntry> getMap() {
+  public Map<Object, CacheEntry<K,V>> getMap() {
     return map;
   }
 
-  private static class CacheEntry implements Comparable<CacheEntry> {
-    Object key, value;
+  static class CacheEntry<K,V> implements Comparable<CacheEntry<K,V>> {
+    K key;
+    V value;
     volatile long lastAccessed = 0;
     long lastAccessedCopy = 0;
 
 
-    public CacheEntry(Object key, Object value, long lastAccessed) {
+    public CacheEntry(K key, V value, long lastAccessed) {
       this.key = key;
       this.value = value;
       this.lastAccessed = lastAccessed;
@@ -439,6 +458,9 @@
 
   public void destroy() {
     stop = true;
+    synchronized(monitor){
+      monitor.notify();
+    }
   }
 
   public Stats getStats() {
@@ -447,7 +469,6 @@
 
   protected void finalize() throws Throwable {
     destroy();
-    super.finalize();
   }
 
   public static class Stats {
@@ -486,4 +507,7 @@
       return missCounter.get();
     }
   }
+  public static interface EvictionListener<K,V>{
+    public void evictedEntry(K key, V value);
+  }
 }
Index: src/java/org/apache/solr/search/FastLRUCache.java
===================================================================
--- src/java/org/apache/solr/search/FastLRUCache.java	(revision 718169)
+++ src/java/org/apache/solr/search/FastLRUCache.java	(working copy)
@@ -69,15 +69,18 @@
     final int initialSize = str == null ? limit : Integer.parseInt(str);
     str = (String) args.get("autowarmCount");
     autowarmCount = str == null ? 0 : Integer.parseInt(str);
+    str = (String) args.get("cleanupThread");
+    boolean newThread = str == null ? false : Boolean.parseBoolean(str);
     
-    description = "Concurrent LRU Cache(maxSize=" + limit + ", initialSize=" + initialSize + ", minSize="+minLimit + ", acceptableSize="+acceptableLimit;
+    description = "Concurrent LRU Cache(maxSize=" + limit + ", initialSize=" + initialSize +
+            ", minSize="+minLimit + ", acceptableSize="+acceptableLimit+" ,cleanupThread ="+newThread;
     if (autowarmCount > 0) {
       description += ", autowarmCount=" + autowarmCount
               + ", regenerator=" + regenerator;
     }
     description += ')';
 
-    cache = new ConcurrentLRUCache(limit, minLimit, acceptableLimit, initialSize, false, false, -1);
+    cache = new ConcurrentLRUCache(limit, minLimit, acceptableLimit, initialSize, newThread, false, null);
     cache.setAlive(false);
 
     if (persistence == null) {
Index: src/test/org/apache/solr/search/TestFastLRUCache.java
===================================================================
--- src/test/org/apache/solr/search/TestFastLRUCache.java	(revision 714218)
+++ src/test/org/apache/solr/search/TestFastLRUCache.java	(working copy)
@@ -72,7 +72,7 @@
     int upperWaterMark = (int)(lowerWaterMark * 1.1);
 
     Random r = new Random(0);
-    ConcurrentLRUCache cache = new ConcurrentLRUCache(upperWaterMark, lowerWaterMark, (upperWaterMark+lowerWaterMark)/2, upperWaterMark, false, false, 0);
+    ConcurrentLRUCache cache = new ConcurrentLRUCache(upperWaterMark, lowerWaterMark, (upperWaterMark+lowerWaterMark)/2, upperWaterMark, false, false, null);
     boolean getSize=false;
     int minSize=0,maxSize=0;
     for (int i=0; i<iter; i++) {
