在LinkedHashMap中,我们知道,LinkedHashMap为我们实现特定替换策略的Map Cache预留了接口,即以如下形式重写removeEldestEntry函数:
private static final int MAX_ENTRIES = 100;
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
但是LinkedHashMap有一点不足在于其实现过程中没有考虑过并发访问的问题,即在多线程环境下对LinkedHashMap进行访问并不安全。
1.Andriod LruCache概述
A cache that holds strong references to a limited number of values. Each time a value is accessed, it is moved to the head of a queue. When a value is added to a full cache, the value at the end of that queue is evicted and may become eligible for garbage collection. If your cached values hold resources that need to be explicitly released, override If a cache miss should be computed on demand for the corresponding keys, override By default, the cache size is measured in the number of entries. Override |
create(K)
用于在访问失效时,为特定的key生成键值的操作,当然这两个函数在源码实现中都是空。
2.底层数据结构支持
private final LinkedHashMap<K, V> map; //LinkedHashMap作为一个成员变量操作
/** Size of this cache in units. Not necessarily the number of elements. */
private int size;
private int maxSize; //Cache元素的个数上限
private int putCount;
private int createCount;
private int evictionCount;
private int hitCount;
private int missCount;
/**
* @param maxSize for caches that do not override {@link #sizeOf}, this is
* the maximum number of entries in the cache. For all other caches,
* this is the maximum sum of the sizes of the entries in this cache.
*/
public LruCache(int maxSize) {
if (maxSize <= 0) { //构造函数,初始化LinkedHashMap
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true); //注意参数,true代表这AccessOrder
}
到这里你会想到,当我们对LruCache进行操作时,都是对其内部封装的LinkedHashMap进行操作。
3.put
/**
* Caches {@code value} for {@code key}. The value is moved to the head of
* the queue.
*
* @return the previous value mapped by {@code key}.
*/
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value); //调用LinkedHashMap的put操作
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize); //缓存容量检测,以保证缓存数据量不超过最大容量
return previous;
}
4.get
/**
* Returns the value for {@code key} if it exists in the cache or can be
* created by {@code #create}. If a value was returned, it is moved to the
* head of the queue. This returns null if a value is not cached and cannot
* be created.
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) { //加锁的get操作
mapValue = map.get(key); //调用LinkedHashMap的get操作
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
/*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/
V createdValue = create(key); //对访问失效的处理,英文注释里说的很清楚,就在于你去怎么处理了
if (createdValue == null) {
return null; //创建不成功直接返回就可以
}
synchronized (this) { //加锁处理,保证第一个被创建的value被缓存,从而保证了缓存数据的一致性
createCount++;
mapValue = map.put(key, createdValue); //当前线程创建成功,进行put操作
if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue); //发现之前已经有线程完成创建操作了,保持原有的缓存value操作
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) { //执行到这里,证明一定发生了替换操作,并且当前线程是后替换操作
entryRemoved(false, key, createdValue, mapValue); //释放当前线程创建的value
return mapValue;
} else {
trimToSize(maxSize); //到这里证明当前线程完成了创建操作,并且当前线程创建的value被缓存
return createdValue; //所以需要进行缓存容量检测
}
}
5.缓存容量控制trimToSize
/**
* @param maxSize the maximum size of the cache before returning. May be -1
* to evict even 0-sized elements.
*/
private void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) { //加锁进行缓存元素的删除操作
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize || map.isEmpty()) {
break;
}
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null); //本线程负责释放已存在元素的空间
}
}