簡單的java緩存實現


掃掃關注“茶爸爸”微信公眾號
堅持最初的執着,從不曾有半點懈怠,為優秀而努力,為證明自己而活。

提到緩存,不得不提就是緩存算法(淘汰算法),常見算法有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;
     }
 
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM