Mega Code Archive

 
Categories / Java / Development Class
 

A thread-safe cache that keeps its values as java lang ref SoftReference so that the cache is, in effect, managed by the

/**  * SoftReferenceCache.java Created Mar 18, 2009 by Andrew Butler, PSL  */ //package prisms.util; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; /**  * A thread-safe cache that keeps its values as {@link java.lang.ref.SoftReference}s so that the  * cache is, in effect, managed by the JVM and kept as small as is required  *   * @param <K> The type of key used to store values with  * @param <V> The type of values to store  */ public class SoftReferenceCache<K, V> implements Map<K, V> {   java.util.HashMap<K, KeyedSoftReference<V>> theCache;   private java.util.concurrent.locks.ReentrantReadWriteLock theLock;   private java.lang.ref.ReferenceQueue<V> theRefQueue;   /**    * Creates a SoftReferenceCache    */   public SoftReferenceCache()   {     theCache = new java.util.HashMap<K, KeyedSoftReference<V>>();     theLock = new java.util.concurrent.locks.ReentrantReadWriteLock();     theRefQueue = new java.lang.ref.ReferenceQueue<V>();   }   /**    * @see java.util.Map#get(java.lang.Object)    */   public V get(Object key)   {     Lock lock = theLock.readLock();     lock.lock();     try     {       KeyedSoftReference<V> val = theCache.get(key);       return val == null ? null : val.get();     } finally     {       lock.unlock();     }   }   /**    * @see java.util.Map#put(java.lang.Object, java.lang.Object)    */   public V put(K key, V value)   {     Lock lock = theLock.writeLock();     lock.lock();     try     {       removeQueued();       KeyedSoftReference<V> val = theCache.get(key);       theCache.put(key, new KeyedSoftReference<V>(key, value, theRefQueue));       return val == null ? null : val.get();     } finally     {       lock.unlock();     }   }   /**    * @see java.util.Map#remove(java.lang.Object)    */   public V remove(Object key)   {     Lock lock = theLock.writeLock();     lock.lock();     try     {       removeQueued();       KeyedSoftReference<V> val = theCache.remove(key);       return val == null ? null : val.get();     } finally     {       lock.unlock();     }   }   /**    * @see java.util.Map#clear()    */   public void clear()   {     Lock lock = theLock.writeLock();     lock.lock();     try     {       theCache.clear();     } finally     {       lock.unlock();     }   }   /**    * @see java.util.Map#size()    */   public int size()   {     return theCache.size();   }   /**    * @see java.util.Map#isEmpty()    */   public boolean isEmpty()   {     return theCache.isEmpty();   }   /**    * @see java.util.Map#containsKey(java.lang.Object)    */   public boolean containsKey(Object key)   {     Lock lock = theLock.readLock();     lock.lock();     try     {       KeyedSoftReference<V> val = theCache.get(key);       return val == null || val.isEnqueued();     } finally     {       lock.unlock();     }   }   /**    * @see java.util.Map#containsValue(java.lang.Object)    */   public boolean containsValue(Object value)   {     throw new UnsupportedOperationException("The containsValue method is not supported by "       + getClass().getName());   }   /**    * @see java.util.Map#putAll(java.util.Map)    */   public void putAll(Map<? extends K, ? extends V> m)   {     Lock lock = theLock.writeLock();     lock.lock();     try     {       removeQueued();       for(java.util.Map.Entry<? extends K, ? extends V> entry : m.entrySet())       {         theCache.put(entry.getKey(), new KeyedSoftReference<V>(entry.getKey(), entry           .getValue(), theRefQueue));       }     } finally     {       lock.unlock();     }   }   /**    * @see java.util.Map#keySet()    */   public Set<K> keySet()   {     Lock lock = theLock.writeLock();     lock.lock();     final Object [] keys;     try     {       removeQueued();       keys = theCache.keySet().toArray();     } finally     {       lock.unlock();     }     return new java.util.AbstractSet<K>()     {       @Override       public Iterator<K> iterator()       {         return new java.util.Iterator<K>()         {           private int index = 0;           public boolean hasNext()           {             return index < keys.length - 1;           }           public K next()           {             index++;             return (K) keys[index - 1];           }           public void remove()           {             SoftReferenceCache.this.remove(keys[index - 1]);           }         };       }       @Override       public int size()       {         return keys.length;       }     };   }   /**    * @see java.util.Map#entrySet()    */   public Set<java.util.Map.Entry<K, V>> entrySet()   {     throw new UnsupportedOperationException("The entrySet method is not supported by "       + getClass().getName());   }   /**    * @see java.util.Map#values()    */   public Collection<V> values()   {     throw new UnsupportedOperationException("The values method is not supported by "       + getClass().getName());   }   /**    * Removes all entries in {@link #theRefQueue} from the map since their references have been    * deleted. The write lock from {@link #theLock} MUST be obtained by the thread before calling    * this method.    */   private void removeQueued()   {     KeyedSoftReference<V> ref = (KeyedSoftReference<V>) theRefQueue.poll();     while(ref != null)     {       theCache.remove(ref.theKey);       ref = (KeyedSoftReference<V>) theRefQueue.poll();     }   }   private class KeyedSoftReference<V2> extends java.lang.ref.SoftReference<V2>   {     final K theKey;     KeyedSoftReference(K key, V2 referent, java.lang.ref.ReferenceQueue<? super V2> queue)     {       super(referent, queue);       theKey = key;     }   } }