前不久,在项目中碰到这样的一个需求:要求在服务初始化的时候把数据库持久化的一些DO对象存入到缓存中,然后每隔指定的时间间隔刷新缓存。同时后台可以让维护人员根据需要手动清除缓存和刷新缓存。由于此需求所应用的缓存规模较小,所以当初就不太想用一些memcached等的缓存数据库,觉得有些过重了。类似的可作缓存使用的mongodb等nosql数据库也是由于附加的应用成本太高被排除。后面自己琢磨着是否针对这个具体的小需求写一个缓存实现。由于是在同一JVM里面,性能问题不是太大的瓶颈,主要是在并发方面考虑了一下,后面参考了jdk5并发包中的ConcurrentHashMap的思想,自己实现了一个简单的缓存。记录一下。
package com.lee.framework.common.cache;
import java.util.concurrent.locks.ReentrantLock;
/**
* A cache implementation can update the cached value automatically which is thread safe.
* you can specified a cache timeout time and a instance of {@link Updater}
* to determine the update action.<br/><br/>
*
* <var>cacheTimeout</var> determines the cached value whether expired or not.<br/>
* <var>updater</var> provides a {@link Updater} implementation to complete the update
* action.<br/><br/>
* besides, there have some customized arguments to satisfy the performance requirement, like follows:<br/><br/>
* <var>initialCapacity</var> provides a way to specify cache initial capacity to avoid frequently capacity
* expansion.<br/>
* <var>concurrencyLevel</var> provides a way to specify the estimated number of concurrently operation
* threads for concurrent performance.<br/><br/>
*
* <b>note:</b> when you get the cached value from this cache by a <var>key</var>, if detect the mapped cache
* value expired, it first acquires a lock, then invoke <var>updater</var> to update the cached value. if this
* operation spend too much time, performance is very poor, so you must avoid it.<br/><br/>
*
* <b>note:</b> when you get the cached value from this cache by a <var>key</var>, if no cache entry mapped the
* <var>key</var> was found, this cache didn't use the <var>updater</var> to automatically update cached value.
* so you must explicitly invoke {@link #set(Object, Object)} to add a cached value mapped for <var>key</var> first,
* then this cache can automatically update it.
*
* @author lee
*
*/
public class AutoUpdateCache {
/** The default initial capacity for this cache **/
private static final int DEFAULT_INITIAL_CAPACITY = 0x10;
/** The default load factor for this cache **/
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
/** The default concurrency level for this cache **/
private static final int DEFAULT_CONCURRENCY_LEVEL = 0x10;
/** The maximum capacity used to ensure that entries are indexable by int type **/
private static final int MAX_CAPACITY = (int) ((1L << 31) - 1);
/** The maximum number of segment permitted for this cache **/
private static final int MAX_SEGMENTS = 1 << 16;
/** encode a <code>null</code> cached value **/
private static final Object NULL = new Object();
/**
* Mask value for indexing into segments.
* The upper bits of a key's hash code are used to choose the segment.
*/
final int segmentMask;
/** Shift value for indexing within segments */
final int segmentShift;
/** each element is a hash table to store the cached value **/
final Segment[] segments;
/** cached value effective time interval **/
final long cacheTimeInterval;
/** updater for cached value **/
final Updater updater;
/** copy from jdk(ConcurrentHashMap)
* Applies a supplemental hash function to a given hashCode, which