http://my.oschina.net/u/866190/blog/188712

提到缓存,不得不提就是缓存算法(淘汰算法),常见算法有LRU、LFU和FIFO等算法,每种算法各有各的优势和缺点及适应环境。

1、LRU(Least Recently Used ,最近最少使用)
算法根据数据的最近访问记录来淘汰数据,其原理是如果数据最近被访问过,将来被访问的几概率相对比较高,最常见的实现是使用一个链表保存缓存数据,详细具体算法如下:
1. 新数据插入到链表头部;
2. 每当缓存数据命中,则将数据移到链表头部;
3. 当链表满的时候,将链表尾部的数据丢弃;

2、LFU(Least Frequently Used,最不经常使用)
算法根据数据的历史访问频率来淘汰数据,其原理是如果数据过去被访问次数越多,将来被访问的几概率相对比较高。LFU的每个数据块都有一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。
具体算法如下:
1. 新加入数据插入到队列尾部(因为引用计数为1);
2. 队列中的数据被访问后,引用计数增加,队列重新排序;
3. 当需要淘汰数据时,将已经排序的列表最后的数据块删除;

3、FIFO(First In First Out ,先进先出)
算法是根据先进先出原理来淘汰数据的,实现上是最简单的一种,具体算法如下:
1. 新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动;
2. 淘汰FIFO队列头部的数据;

评价一个缓存算法好坏的标准主要有两个,一是命中率要高,二是算法要容易实现。当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。LFU效率要优于LRU,且能够避免周期性或者偶发性的操作导致缓存命中率下降的问题。但LFU需要记录数据的历史访问记录,一旦数据访问模式改变,LFU需要更长时间来适用新的访问模式,即:LFU存在历史数据影响将来数据的“缓存污染”效用。FIFO虽然实现很简单,但是命中率很低,实际上也很少使用这种算法。

基于现有jdk类库,我们可以很容易实现上面的缓存算法

首先定义缓存接口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
 * 缓存接口
 * @author Wen
 *
 */
public interface Cache<K,V> {
    /**
     * 返回当前缓存的大小
     *
     * @return 
     */
    int size();
     
    /**
     * 返回默认存活时间
     *
     * @return
     */
    long getDefaultExpire();
     
    /**
     * 向缓存添加value对象,其在缓存中生存时间为默认值
     *
     * @param key
     * @param value
     */
    void put(K key ,V value) ;
     
    /**
     * 向缓存添加value对象,并指定存活时间
     * @param key
     * @param value
     * @param expire  过期时间
     */
    void put(K key ,V value , long expire ) ;
     
    /**
     * 查找缓存对象
     * @param key
     * @return
     */
    V get(K key);
     
    /**
     * 淘汰对象
     *
     * @return  被删除对象大小
     */
    int eliminate();
     
    /**
     * 缓存是否已经满
     * @return
     */
    boolean isFull();
 
    /**
     * 删除缓存对象
     *
     * @param key
     */
    void remove(K key);
 
    /**
     * 清除所有缓存对象
     */
    void clear();
 
    /**
     * 返回缓存大小
     *
     * @return 
     */
    int getCacheSize();
 
    /**
     * 缓存中是否为空
     */
    boolean isEmpty();
 
}

基本实现抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
/**
 * 默认实现
 */
public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {
 
    class CacheObject<K2,V2> {
        CacheObject(K2 key, V2 value, long ttl) {
            this.key = key;
            this.cachedObject = value;
            this.ttl = ttl;
            this.lastAccess = System.currentTimeMillis();
        }
 
        final K2 key;
        final V2 cachedObject;
        long lastAccess;        // 最后访问时间
        long accessCount;       // 访问次数
        long ttl;               // 对象存活时间(time-to-live)
 
        boolean isExpired() {
            if (ttl == 0) {
                return false;
            }
            return lastAccess + ttl < System.currentTimeMillis();
        }
        V2 getObject() {
            lastAccess = System.currentTimeMillis();
            accessCount++;
            return cachedObject;
        }
    }
 
    protected Map<K,CacheObject<K,V>> cacheMap;
 
    private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
    private final Lock readLock = cacheLock.readLock();
    private final Lock writeLock = cacheLock.writeLock();
 
 
 
    protected int cacheSize;      // 缓存大小 , 0 -> 无限制
     
    protected  boolean existCustomExpire ; //是否设置默认过期时间
     
    public int getCacheSize() {
        return cacheSize;
    }
 
    protected long defaultExpire;     // 默认过期时间, 0 -> 永不过期
     
    public AbstractCacheMap(int cacheSize ,long defaultExpire){
        this.cacheSize  = cacheSize ;
        this.defaultExpire  = defaultExpire ;
    }
 
     
    public long getDefaultExpire() {
        return defaultExpire;
    }
 
 
    protected boolean isNeedClearExpiredObject(){
        return defaultExpire > 0 || existCustomExpire ;
    }
 
     
    public void put(K key, V value) {
        put(key, value, defaultExpire );
    }
 
 
    public void put(K key, V value, long expire) {
        writeLock.lock();
 
        try {
            CacheObject<K,V> co = new CacheObject<K,V>(key, value, expire);
            if (expire != 0) {
                existCustomExpire = true;
            }
            if (isFull()) {
                eliminate() ;
            }
            cacheMap.put(key, co);
        }
        finally {
            writeLock.unlock();
        }
    }
 
 
 
    /**
     * {@inheritDoc}
     */
    public V get(K key) {
        readLock.lock();
 
        try {
            CacheObject<K,V> co = cacheMap.get(key);
            if (co == null) {
                return null;
            }
            if (co.isExpired() == true) {
                cacheMap.remove(key);
                return null;
            }
 
            return co.getObject();
        }
        finally {
            readLock.unlock();
        }
    }
     
    public final int eliminate() {
        writeLock.lock();
        try {
            return eliminateCache();
        }
        finally {
            writeLock.unlock();
        }
    }
     
    /**
     * 淘汰对象具体实现
     *
     * @return
     */
    protected abstract int eliminateCache();
 
 
     
    public boolean isFull() {
        if (cacheSize == 0) {//o -> 无限制
            return false;
        }
        return cacheMap.size() >= cacheSize;
    }
 
     
    public void remove(K key) {
        writeLock.lock();
        try {
            cacheMap.remove(key);
        }
        finally {
            writeLock.unlock();
        }
    }
 
     
    public void clear() {
        writeLock.lock();
        try {
            cacheMap.clear();
        }
        finally {
            writeLock.unlock();
        }
    }
 
    public int size() {
        return cacheMap.size();
    }
 
     
    public boolean isEmpty() {
        return size() == 0;
    }
}

LRU缓存实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
 
/**
 * LRU  实现
 * @author Wen
 *
 * @param <K>
 * @param <V>
 */
public class LRUCache<K, V> extends AbstractCacheMap<K, V> {
 
    public LRUCache(int cacheSize, long defaultExpire) {
         
        super(cacheSize , defaultExpire) ;
 
        //linkedHash已经实现LRU算法 是通过双向链表来实现,当某个位置被命中,通过调整链表的指向将该位置调整到头位置,新加入的内容直接放在链表头,如此一来,最近被命中的内容就向链表头移动,需要替换时,链表最后的位置就是最近最少使用的位置
        this.cacheMap = new LinkedHashMap<K, CacheObject<K, V>>( cacheSize +1 , 1f,true ) {
 
            @Override
            protected boolean removeEldestEntry(
                    Map.Entry<K, CacheObject<K, V>> eldest) {
 
                return LRUCache.this.removeEldestEntry(eldest);
            }
 
        };
    }
 
    private boolean removeEldestEntry(Map.Entry<K, CacheObject<K, V>> eldest) {
 
        if (cacheSize == 0)
            return false;
 
        return size() > cacheSize;
    }
 
    /**
     * 只需要实现清除过期对象就可以了,linkedHashMap已经实现LRU
     */
    @Override
    protected int eliminateCache() {
 
        if(!isNeedClearExpiredObject()){ return 0 ;}
         
        Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();
        int count  = 0 ;
        while(iterator.hasNext()){
            CacheObject<K, V> cacheObject = iterator.next();
             
            if(cacheObject.isExpired() ){
                iterator.remove();
                count++ ;
            }
        }
         
        return count;
    }
 
}

LFU实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import java.util.HashMap;
import java.util.Iterator;
 
//LFU实现
public class LFUCache<K,V> extends AbstractCacheMap<K, V> {
     
 
    public LFUCache(int cacheSize, long defaultExpire) {
        super(cacheSize, defaultExpire);
        cacheMap = new HashMap<K, CacheObject<K,V>>(cacheSize+1) ;
    }
 
    /**
     * 实现删除过期对象 和 删除访问次数最少的对象
     *
     */
    @Override
    protected int eliminateCache() {
        Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();
        int count  = 0 ;
        long minAccessCount = Long.MAX_VALUE  ;
        while(iterator.hasNext()){
            CacheObject<K, V> cacheObject = iterator.next();
             
            if(cacheObject.isExpired() ){
                iterator.remove();
                count++ ;
                continue ;
            }else{
                minAccessCount  = Math.min(cacheObject.accessCount , minAccessCount)  ;
            }
        }
         
        if(count > 0 ) return count ;
         
        if(minAccessCount != Long.MAX_VALUE ){
             
            iterator = cacheMap.values().iterator();
             
            while(iterator.hasNext()){
                CacheObject<K, V> cacheObject = iterator.next();
                 
                cacheObject.accessCount  -=  minAccessCount ;
                 
                if(cacheObject.accessCount <= 0 ){
                    iterator.remove();
                    count++ ;
                }
                 
            }
             
        }
         
        return count;
    }
 
}

FIFO实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.util.Iterator;
import java.util.LinkedHashMap;
/**
 * FIFO实现
 * @author Wen
 *
 * @param <K>
 * @param <V>
 */
public class FIFOCache<K, V> extends AbstractCacheMap<K, V> {
 
    public FIFOCache(int cacheSize, long defaultExpire) {
        super(cacheSize, defaultExpire);
        cacheMap = new LinkedHashMap<K, CacheObject<K, V>>(cacheSize + 1);
    }
 
    @Override
    protected int eliminateCache() {
 
        int count = 0;
        K firstKey = null;
 
        Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();
        while (iterator.hasNext()) {
            CacheObject<K, V> cacheObject = iterator.next();
 
            if (cacheObject.isExpired()) {
                iterator.remove();
                count++;
            } else {
                if (firstKey == null)
                    firstKey = cacheObject.key;
            }
        }
 
        if (firstKey != null && isFull()) {//删除过期对象还是满,继续删除链表第一个
            cacheMap.remove(firstKey);
        }
 
        return count;
    }
 
}

 

java缓存算法【转】的更多相关文章

  1. 【Java/Android性能优 6】Android 图片SD卡缓存 使用简单 支持预取 支持多种缓存算法 支持不同网络类型 支持序列化

    本文转自:http://www.trinea.cn/android/android-imagesdcardcache/ 本文主要介绍一个支持图片自动预取.支持多种缓存算法.支持数据保存和恢复的图片Sd ...

  2. 【Java/Android性能优 4】PreloadDataCache支持预取的数据缓存,使用简单,支持多种缓存算法,支持不同网络类型,扩展性强

    本文转自:http://www.trinea.cn/android/preloaddatacache/ 本文主要介绍一个支持自动向前或向后获取新数据的缓存的使用及功能.Android图片内存缓存可见I ...

  3. 【Java/Android性能优5】 Android ImageCache图片缓存,使用简单,支持预取,支持多种缓存算法,支持不同网络类型,扩展性强

    本文转自:http://www.trinea.cn/android/android-imagecache/ 主要介绍一个支持图片自动预取.支持多种缓存算法.支持二级缓存.支持数据保存和恢复的图片缓存的 ...

  4. Android ImageCache图片缓存,使用简单,支持预取,支持多种缓存算法,支持不同网络类型,扩展性强

    本文主要介绍一个支持图片自动预取.支持多种缓存算法的图片缓存的使用及功能.图片较大需要SD卡保存情况推荐使用ImageSDCardCache. 与Android LruCache相比主要特性:(1). ...

  5. 简单的java缓存实现

    扫扫关注"茶爸爸"微信公众号 坚持最初的执着,从不曾有半点懈怠,为优秀而努力,为证明自己而活. 提到缓存,不得不提就是缓存算法(淘汰算法),常见算法有LRU.LFU和FIFO等算法 ...

  6. 【转】简单的java缓存实现

    本文转自 http://my.oschina.net/u/866190/blog/188712 提到缓存,不得不提就是缓存算法(淘汰算法),常见算法有LRU.LFU和FIFO等算法,每种算法各有各的优 ...

  7. java排序算法(十):桶式排序

    java排序算法(十):桶式排序 桶式排序不再是一种基于比较的排序方法,它是一种比较巧妙的排序方式,但这种排序方式需要待排序的序列满足以下两个特征: 待排序列所有的值处于一个可枚举的范围之类: 待排序 ...

  8. java排序算法(七):折半插入排序

    java排序算法(七):折半插入排序 折半插入排序法又称为二分插入排序法,是直接插入排序法的改良版本,也需要执行i-1趟插入.不同之处在于第i趟插入.先找出第i+1个元素应该插入的位置.假设前i个数据 ...

  9. java排序算法(六):直接插入排序

    java排序算法(六):直接插入排序 直接插入排序的基本操作就是将待的数据元素按其关键字的大小插入到前面的有序序列中 直接插入排序时间效率并不高,如果在最坏的情况下,所有元素的比较次数的总和为(0+1 ...

随机推荐

  1. easyui源码翻译1.32--Calendar(日历)

    前言 前几天加班比较忙 未能及时更新翻译的 今天多发布几篇..下载该插件翻译源码 日历控件显示一个月的日历,允许用户选择日期和移动到下一个或上一个月.默认情况下,一周的第一天是周日.它可以通过设置'f ...

  2. jacob访问ocx控件方法和遇到的问题

    最近在进行摄像机的二次开发,摄像机厂商提供了使用C++开发的ocx控件:所以尝试使用jacob来进行访问. 操作步骤如下: 1, 从官网(http://sourceforge.net/projects ...

  3. Delphi 中的DLL 封装和调用对象技术(刘艺,有截图)

    Delphi 中的DLL 封装和调用对象技术本文刊登2003 年10 月份出版的Dr.Dobb's 软件研发第3 期刘 艺摘 要DLL 是一种应用最为广泛的动态链接技术但是由于在DLL 中封装和调用对 ...

  4. Android 使控件各占屏幕的一半

    在xml中将两个要占屏幕一半的控件都加上android:layout_weight="1": 注意:weight只能用在LinearLayout布局中. 在LinearLayout ...

  5. js中replace用法

    js中replace的用法 replace方法的语法是:stringObj.replace(rgExp, replaceText) 其中stringObj是字符串(string),reExp可以是正则 ...

  6. Case swapping

    Case swapping Description: Given a string, swap the case for each of the letters. e.g. CodEwArs --&g ...

  7. Oracle系列之视图

    涉及到表的处理请参看原表结构与数据  Oracle建表插数据等等 创建视图,把tb_Employee表sal<1000的雇员,映射到该视图( view) create or replace vi ...

  8. bzoj1976

    终于忙完期末考试了,即将进入愉快的暑假(虽然暑假作业奇多,但好歹终于能有大量时间刷题了) 先把上次新一类最小割留下的一道题目A了复习一下: 题目看起来很复杂,实际上和bzoj2132是同一个类型的 用 ...

  9. 关于Sublime Text2 GBK编码的问题

    很多文章都说需要"ConvertToUTF8"和"GBK Encoding Support"连个插件. 其实GBK Encoding Support完全不需要, ...

  10. html5 svg 第八章 文字text

    虽然它可能是真实的,每一个画面讲述了一个故事,这是完全正确的,用言语来帮助讲故事.因此,SVG有几个元素,让你将文本添加到您的图形. 文本术语 Text Terminology 在我们调查的主要方法添 ...