Android LruCache初探
来源:互联网 发布:php jquery post json 编辑:程序博客网 时间:2024/04/27 13:47
在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 entryRemoved(boolean, K, V, V)
.
If a cache miss should be computed on demand for the corresponding keys, override create(K)
. This simplifies the calling code, allowing it to assume a value will always be returned, even when there's a cache miss.
By default, the cache size is measured in the number of entries. Override sizeOf(K, V)
to size the cache in different units.
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) { //构造函数,初始化LinkedHashMapthrow 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 putmap.put(key, mapValue); //发现之前已经有线程完成创建操作了,保持原有的缓存value操作} else {size += safeSizeOf(key, createdValue);}}if (mapValue != null) { //执行到这里,证明一定发生了替换操作,并且当前线程是后替换操作entryRemoved(false, key, createdValue, mapValue); //释放当前线程创建的valuereturn 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); //本线程负责释放已存在元素的空间}}
5.小结
- Android LruCache初探
- Android LruCache
- Android LruCache
- android LruCache
- Android Memory Cache: LruCache
- android.util.LruCache
- android源代码之LruCache
- Android LruCache 缓存图片
- Android LruCache类
- android LruCache类解读
- Android LruCache类分析
- android LruCache存储
- Android LruCache使用分析
- Android LruCache源码介绍
- android:LruCache缓存小结
- Android Lrucache类注意事项
- Android LruCache用于Bitmap
- Android缓存-LruCache分析
- twisted-01 Echo server and echo client
- CCS调试CSL_intc GlobalEnable,first referenced in ./xxx.obj解决方案
- CodeForces 1B-Spreadsheet
- 数据结构入门——递归
- Web Service修炼之四WS-Security
- Android LruCache初探
- 网络编程系列之八 unp.h环境
- Apache与Nginx的优缺点比较
- [乐意黎原创] php 页面提示 Fatal error: Call to undefined function curl_init()
- Qt 删除资源文件后提示找不到相应文件
- Service基本讲解
- 人人都看得懂的正则表达式教程
- Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
- android:descendantFocusability用法简析