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是怎么作的,能够给咱们一个参考