2021SC@SDUSC
cache.CacheStorage and its subclasses
Overview map
1.CacheStorage interface
code
public interface CacheStorage { public Object get(Object key); public void put(Object key, Object value); public void remove(Object key); public void clear(); }
Function: Cache storage abstracts the storage aspect of cache - associate object s with keys, and retrieve and delete them through keys. It is actually a sub interface of java.util.Map.
2.CacheStorageWithGetSize interface
code
public interface CacheStorageWithGetSize extends CacheStorage { int getSize(); }
Function: the cache store with getSize() method is used to return the number of current cache entries.
3. Concurrent cache storage interface
code
public interface ConcurrentCacheStorage extends CacheStorage { public boolean isConcurrent(); }
Function: an optional cache storage interface, which knows whether it can be accessed concurrently without synchronization.
4.NullCacheStorage class
code
public class NullCacheStorage implements ConcurrentCacheStorage, CacheStorageWithGetSize { public static final NullCacheStorage INSTANCE = new NullCacheStorage(); @Override public boolean isConcurrent() { return true; } @Override public Object get(Object key) { return null; } @Override public void put(Object key, Object value) { // do nothing } @Override public void remove(Object key) { // do nothing } @Override public void clear() { // do nothing } public int getSize() { return 0; } }
Function: a cache memory that does not store anything. If you don't need caching, you can use this.
5.SoftCacheStorage class
code
public class SoftCacheStorage implements ConcurrentCacheStorage, CacheStorageWithGetSize { private static final Method atomicRemove = getAtomicRemoveMethod(); private final ReferenceQueue queue = new ReferenceQueue(); private final Map map; private final boolean concurrent; public SoftCacheStorage() { this(new ConcurrentHashMap()); } @Override public boolean isConcurrent() { return concurrent; } public SoftCacheStorage(Map backingMap) { map = backingMap; this.concurrent = map instanceof ConcurrentMap; } @Override public Object get(Object key) { processQueue(); Reference ref = (Reference) map.get(key); return ref == null ? null : ref.get(); } @Override public void put(Object key, Object value) { processQueue(); map.put(key, new SoftValueReference(key, value, queue)); } @Override public void remove(Object key) { processQueue(); map.remove(key); } @Override public void clear() { map.clear(); processQueue(); } @Override public int getSize() { processQueue(); return map.size(); } private void processQueue() { for (; ; ) { SoftValueReference ref = (SoftValueReference) queue.poll(); if (ref == null) { return; } Object key = ref.getKey(); if (concurrent) { try { atomicRemove.invoke(map, new Object[] { key, ref }); } catch (IllegalAccessException | InvocationTargetException e) { throw new UndeclaredThrowableException(e); } } else if (map.get(key) == ref) { map.remove(key); } } } private static final class SoftValueReference extends SoftReference { private final Object key; SoftValueReference(Object key, Object value, ReferenceQueue queue) { super(value, queue); this.key = key; } Object getKey() { return key; } } private static Method getAtomicRemoveMethod() { try { return Class.forName("java.util.concurrent.ConcurrentMap").getMethod("remove", new Class[] { Object.class, Object.class }); } catch (ClassNotFoundException e) { return null; } catch (NoSuchMethodException e) { throw new UndeclaredThrowableException(e); } } }
Function: soft cache storage is a kind of cache storage that uses the SoftReference object to save the objects passed to it, so it allows the garbage collector to clear the cache when it determines that it wants to free memory. This class has the same level of thread safety as its underlying mapping. The parameterless constructor uses thread safe mapping from 2.3.24 or Java 5 or above.
6.StrongCacheStorage class
code
public class StrongCacheStorage implements ConcurrentCacheStorage, CacheStorageWithGetSize { private final Map map = new ConcurrentHashMap(); @Override public boolean isConcurrent() { return true; } @Override public Object get(Object key) { return map.get(key); } @Override public void put(Object key, Object value) { map.put(key, value); } @Override public void remove(Object key) { map.remove(key); } @Override public int getSize() { return map.size(); } @Override public void clear() { map.clear(); } }
Function: strong cache storage is a cache storage that simply encapsulates Map. It holds strong references to all objects passed to it, thus preventing the cache from being cleared during garbage collection.
7.MruCacheStorage class
code
public class MruCacheStorage implements CacheStorageWithGetSize { private final MruEntry strongHead = new MruEntry(); private final MruEntry softHead = new MruEntry(); { softHead.linkAfter(strongHead); } private final Map map = new HashMap(); private final ReferenceQueue refQueue = new ReferenceQueue(); private final int strongSizeLimit; private final int softSizeLimit; private int strongSize = 0; private int softSize = 0; public MruCacheStorage(int strongSizeLimit, int softSizeLimit) { if (strongSizeLimit < 0) throw new IllegalArgumentException("strongSizeLimit < 0"); if (softSizeLimit < 0) throw new IllegalArgumentException("softSizeLimit < 0"); this.strongSizeLimit = strongSizeLimit; this.softSizeLimit = softSizeLimit; } @Override public Object get(Object key) { removeClearedReferences(); MruEntry entry = (MruEntry) map.get(key); if (entry == null) { return null; } relinkEntryAfterStrongHead(entry, null); Object value = entry.getValue(); if (value instanceof MruReference) { // This can only happen with strongSizeLimit == 0 return ((MruReference) value).get(); } return value; } @Override public void put(Object key, Object value) { removeClearedReferences(); MruEntry entry = (MruEntry) map.get(key); if (entry == null) { entry = new MruEntry(key, value); map.put(key, entry); linkAfterStrongHead(entry); } else { relinkEntryAfterStrongHead(entry, value); } } @Override public void remove(Object key) { removeClearedReferences(); removeInternal(key); } private void removeInternal(Object key) { MruEntry entry = (MruEntry) map.remove(key); if (entry != null) { unlinkEntryAndInspectIfSoft(entry); } } @Override public void clear() { strongHead.makeHead(); softHead.linkAfter(strongHead); map.clear(); strongSize = softSize = 0; // Quick refQueue processing while (refQueue.poll() != null); } private void relinkEntryAfterStrongHead(MruEntry entry, Object newValue) { if (unlinkEntryAndInspectIfSoft(entry) && newValue == null) { // Turn soft reference into strong reference, unless is was cleared MruReference mref = (MruReference) entry.getValue(); Object strongValue = mref.get(); if (strongValue != null) { entry.setValue(strongValue); linkAfterStrongHead(entry); } else { map.remove(mref.getKey()); } } else { if (newValue != null) { entry.setValue(newValue); } linkAfterStrongHead(entry); } } private void linkAfterStrongHead(MruEntry entry) { entry.linkAfter(strongHead); if (strongSize == strongSizeLimit) { // softHead.previous is LRU strong entry MruEntry lruStrong = softHead.getPrevious(); // Attila: This is equaivalent to strongSizeLimit != 0 // DD: But entry.linkAfter(strongHead) was just executed above, so // lruStrong != strongHead is true even if strongSizeLimit == 0. if (lruStrong != strongHead) { lruStrong.unlink(); if (softSizeLimit > 0) { lruStrong.linkAfter(softHead); lruStrong.setValue(new MruReference(lruStrong, refQueue)); if (softSize == softSizeLimit) { // List is circular, so strongHead.previous is LRU soft entry MruEntry lruSoft = strongHead.getPrevious(); lruSoft.unlink(); map.remove(lruSoft.getKey()); } else { ++softSize; } } else { map.remove(lruStrong.getKey()); } } } else { ++strongSize; } } private boolean unlinkEntryAndInspectIfSoft(MruEntry entry) { entry.unlink(); if (entry.getValue() instanceof MruReference) { --softSize; return true; } else { --strongSize; return false; } } private void removeClearedReferences() { for (; ; ) { MruReference ref = (MruReference) refQueue.poll(); if (ref == null) { break; } removeInternal(ref.getKey()); } } public int getStrongSizeLimit() { return strongSizeLimit; } public int getSoftSizeLimit() { return softSizeLimit; } public int getStrongSize() { return strongSize; } public int getSoftSize() { removeClearedReferences(); return softSize; } @Override public int getSize() { return getSoftSize() + getStrongSize(); } private static final class MruEntry { private MruEntry prev; private MruEntry next; private final Object key; private Object value; MruEntry() { makeHead(); key = value = null; } MruEntry(Object key, Object value) { this.key = key; this.value = value; } Object getKey() { return key; } Object getValue() { return value; } void setValue(Object value) { this.value = value; } MruEntry getPrevious() { return prev; } void linkAfter(MruEntry entry) { next = entry.next; entry.next = this; prev = entry; next.prev = this; } void unlink() { next.prev = prev; prev.next = next; prev = null; next = null; } void makeHead() { prev = next = this; } } private static class MruReference extends SoftReference { private final Object key; MruReference(MruEntry entry, ReferenceQueue queue) { super(entry.getValue(), queue); this.key = entry.getKey(); } Object getKey() { return key; } } }
Function: realize the cache storage of two-level recently used cache. In the first layer, the item is strongly referenced to the specified maximum value. When the maximum value is exceeded, the least recently used items are moved to the second level cache, where they are soft referenced until another second level cache, where they are soft referenced until another recently used item is completely discarded. This cache storage is a generalization of StrongCacheStorage and SoftCacheStorage - both effects can be achieved by setting a positive integer with a maximum value of zero and the other maximum value of maximum.
Note: Freemarker code comes from FreeMarker Chinese official reference manual
Novice code analysis, if there are errors in the article, please point out