google的guava可謂鼎鼎有名,最近在研究緩存,也就看看它是怎麼處理緩存過時問題的;首先它並無經過在後臺起一個線程,不停去輪詢。不這麼作主要是爲了效率吧,也就是所謂的惰性移除,在get時判斷是否過時。那若是一直不訪問,可能存在內存泄漏問題。java
示例代碼:算法
Cache<Object, Object> cache = CacheBuilder.newBuilder().expireAfterAccess(2,TimeUnit.SECONDS).build(); cache.put("a",1); System.out.println(cache.getIfPresent("a")); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(cache.getIfPresent("a"));
1,CacheBuilder默認的緩存實現爲LocalCache,因此這裏咱們主要去研究LocalCache的getIfPresent 便可緩存
2,經過觀察,咱們能夠猜出LocalCache 是用相似於ConcurrentHashMap 的數據結構來保存數據的數據結構
3,這裏咱們主要看其Segment 的get 方法,而後進入getLiveEntry 方法,看名字感受跟存活有關,點進去post
ReferenceEntry<K, V> getLiveEntry(Object key, int hash, long now) { //獲取值 ReferenceEntry<K, V> e = getEntry(key, hash); if (e == null) {//若是爲空,返回空 return null; } else if (map.isExpired(e, now)) {//判斷是否過時 tryExpireEntries(now); return null; } return e; } boolean isExpired(ReferenceEntry<K, V> entry, long now) { checkNotNull(entry); if (expiresAfterAccess() && (now - entry.getAccessTime() >= expireAfterAccessNanos)) { return true; } if (expiresAfterWrite() && (now - entry.getWriteTime() >= expireAfterWriteNanos)) { return true; } return false; }
maximumSize做用原理,猜測:應該是在put緩存時,檢查是否達到了最大值,若是達到則用LRU算法移除一個cacheui
1,觀察LocalCache#putgoogle
V put(K key, int hash, V value, boolean onlyIfAbsent) { lock(); try { ... evictEntries(newEntry);//這行代碼,看名字感受就是它,點進去看 return null; } finally { unlock(); postWriteCleanup(); } } void evictEntries(ReferenceEntry<K, V> newest) { .... while (totalWeight > maxSegmentWeight) { ReferenceEntry<K, V> e = getNextEvictable(); if (!removeEntry(e, e.getHash(), RemovalCause.SIZE)) { throw new AssertionError(); } } }
這裏咱們不妨看看它的LRU算法是如何實現的線程
ReferenceEntry<K, V> getNextEvictable() { for (ReferenceEntry<K, V> e : accessQueue) { int weight = e.getValueReference().getWeight(); if (weight > 0) { return e; } } throw new AssertionError(); }
發現它是用隊列實現的,也就是在插入新緩存是有排序。code
總結:排序
咱們時常會有本地緩存的需求,這時不妨看看google是怎麼作的,能夠給咱們一個參考