【java互联网架构】手把手教你实现线程安全并且可以设置过期时间的LRU缓存安排!( 三 )


非并发环境测试:

【java互联网架构】手把手教你实现线程安全并且可以设置过期时间的LRU缓存安排!
本文插图

并发环境测试:
我们初始化了一个固定容量为 10 的线程池和count为10的CountDownLatch 。 我们将100次操作分10次添加到线程池, 然后我们等待线程池执行完成这10次操作 。

【java互联网架构】手把手教你实现线程安全并且可以设置过期时间的LRU缓存安排!
本文插图

6. 实现一个线程安全并且带有过期时间的 LRU 缓存
实际上就是在我们上面时间的LRU缓存的基础上加上一个定时任务去删除缓存 , 常见的实现定时任务的方式有下面几种:
Timer :不被推荐 , 多线程会存在问题 。
ScheduledExecutorService :定时器线程池 , 可以用来替代 Timer
DelayQueue :延时队列
quartz :一个很火的开源任务调度框架 , 很多其他框架都是基于 quartz 开发的 , 比如当当网的elastic-job就是基于quartz二次开发之后的分布式调度解决方案
......
最终我们选择了 ScheduledExecutorService , 主要原因是它易用(基于DelayQueue做了很多封装)并且基本能满足我们的大部分需求 。
我们在我们上面实现的线程安全的 LRU 缓存基础上 , 简单稍作修改即可!我们增加了一个方法:

【java互联网架构】手把手教你实现线程安全并且可以设置过期时间的LRU缓存安排!
本文插图

我们put元素的时候 , 如果通过这个方法就能直接设置过期时间 。
完整源码如下:/** *@authorshuang.kou *
*使用ConcurrentHashMap+ConcurrentLinkedQueue+ReadWriteLock+ScheduledExecutorService实现线程安全的LRU缓存 *这里只是为了学习使用 , 本地缓存推荐使用Guava自带的 , 使用Spring的话 , 推荐使用SpringCache */ publicclassMyLruCacheWithExpireTime
{ /** *缓存的最大容量 */ privatefinalintmaxCapacity; privateConcurrentHashMap
cacheMap; privateConcurrentLinkedQueuekeys; /** *读写锁 */ privateReadWriteLockreadWriteLock=newReentrantReadWriteLock(); privateLockwriteLock=readWriteLock.writeLock(); privateLockreadLock=readWriteLock.readLock(); privateScheduledExecutorServicescheduledExecutorService; publicMyLruCacheWithExpireTime(intmaxCapacity){ if(maxCapacity
(maxCapacity); keys=newConcurrentLinkedQueue
0){ removeAfterExpireTime(key,expireTime); } returnvalue; }finally{ writeLock.unlock(); } } publicVget(Kkey){ //加读锁 readLock.lock(); try{ //key是否存在于当前缓存 if(cacheMap.containsKey(key)){ //存在的话就将key移动到队列的尾部 moveToTailOfQueue(key); returncacheMap.get(key); } //不存在于当前缓存中就返回Null returnnull; }finally{ readLock.unlock(); } } publicVremove(Kkey){ writeLock.lock(); try{ //key是否存在于当前缓存 if(cacheMap.containsKey(key)){ //存在移除队列和Map中对应的Key keys.remove(key); returncacheMap.remove(key); } //不存在于当前缓存中就返回Null returnnull; }finally{ writeLock.unlock(); } } /** *将元素添加到队列的尾部(put/get的时候执行) */ privatevoidmoveToTailOfQueue(Kkey){ keys.remove(key); keys.add(key); } /** *移除队列头部的元素以及其对应的缓存(缓存容量已满的时候执行) */ privatevoidremoveOldestKey(){ KoldestKey=keys.poll(); if(oldestKey!=null){ cacheMap.remove(oldestKey); } } privatevoidremoveAfterExpireTime(Kkey,longexpireTime){ scheduledExecutorService.schedule(()->{ //过期后清除该键值对 cacheMap.remove(key); keys.remove(key); },expireTime,TimeUnit.MILLISECONDS); } publicintsize(){ returncacheMap.size(); } }


推荐阅读