扫扫关注“茶爸爸”微信公众号
坚持最初的执着,从不曾有半点懈怠,为优秀而努力,为证明自己而活。

提到缓存,不得不提就是缓存算法(淘汰算法),常见算法有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缓存实现

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

  2. 更好用 更简单的Java缓存框架 jscache

    比Spring Cache 更好用 更简单的缓存工具 jscache 取名意义为 java simple cache,基于AOP实现,支持注解到接口 自定义单个缓存过期时间配置 ttl,轻松扩展缓存实 ...

  3. Map实现java缓存机制的简单实例

    缓存是Java中主要的内容,主要目的是缓解项目访问数据库的压力以及提升访问数据的效率,以下是通过Map实现java缓存的功能,并没有用cache相关框架. 一.缓存管理类 CacheMgr.java ...

  4. (转)java缓存技术,记录

    http://blog.csdn.net/madun/article/details/8569860 最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇 ...

  5. JAVA缓存技术

    介绍 JNotify:http://jnotify.sourceforge.net/,通过JNI技术,让Java代码可以实时的监控制定文件夹内文件的变动信息,支持Linux/Windows/MacOS ...

  6. JAVA缓存技术之EhCache

    最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇,暂作保存,后面如果有用到可以参考.此为转贴,帖子来处:http://cogipard.info/ar ...

  7. Java缓存

    Java中要用到缓存的地方很多,首当其冲的就是持久层缓存,针对持久层谈一下: 要实现java缓存有很多种方式,最简单的无非就是static HashMap,这个显然是基于内存缓存,一个map就可以搞定 ...

  8. JAVA缓存技术之EhCache(转)

    最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇,暂作保存,后面如果有用到可以参考.此为转贴,帖子来处:http://cogipard.info/ar ...

  9. 简单聊聊java中的final关键字

    简单聊聊java中的final关键字 日常代码中,final关键字也算常用的.其主要应用在三个方面: 1)修饰类(暂时见过,但是还没用过); 2)修饰方法(见过,没写过); 3)修饰数据. 那么,我们 ...

随机推荐

  1. maven项目启动

    1服务install 2 build (tomcat:run)

  2. Mysql 正则表达式 判断字段值不包含数字

    SELECT * FROM (select replace(FlightId_IaTa,LEFT(FlightId_IaTa,2),'') as aa,FlightId_IaTa,FlightIdfr ...

  3. HTTP协议头部与Keep-Alive模式详解

    HTTP协议头部与Keep-Alive模式详解 .什么是Keep-Alive模式? 我们知道HTTP协议采用“请求-应答”模式,当使用普通模式,即非KeepAlive模式时,每个请求/应答客户和服务器 ...

  4. ES6学习笔记:Module的基本用法

    export和import ES6实现了模块功能,试图解决JavaScript代码上的依赖和部署上的问题,取代现有的CommonJs的AMD规范,成为浏览器和服务器通用的模块解决方案. 模块功能有两个 ...

  5. poj 1041 John's trip 欧拉回路

    题目链接 求给出的图是否存在欧拉回路并输出路径, 从1这个点开始, 输出时按边的升序输出. 将每个点的边排序一下就可以. #include <iostream> #include < ...

  6. FPGA工程中用C语言对文件进行处理_生成mif文件

    本博客中有用verilog处理文件数据的代码,本博文采用C 处理文件中的数据. 有时候要生成一个mif文件—— altera memory  initial file.本次工程中我得到的是一个大型的数 ...

  7. javascript 字符串方法传参

    javascript 字符串方法传参由于嵌套的单引号,双引号过多.有点混乱.. 正确方法如下: '   <td align="left"><input type= ...

  8. uva11536 Smallest Sub-Array

    Thinking about it: 我的思路跟sliding window有点类似.假设已经确定了一个区间[l, r],序列中从 l 到 r 恰好包含了[1, K]的各个元素,则从 r 开始继续迭代 ...

  9. BZOJ 1062

    program candy bzoj1062; ; maxm=; maxn=; var n,len,m,i,p,t,l,r,c,d,q:longint; s:..,..maxn,..maxm] of ...

  10. android使用全局变量的两种方法

         在我们使用android编写程序的时候,少不了想利用全局变量,但是面向对象语言和过程语言区别很大,不再是include就可以的.这里我写了使用全局变量的两种方法: 1.使用applicati ...