【java互联网架构】手把手教你实现线程安全并且可以设置过期时间的LRU缓存安排!( 二 )
ScheduledThreadPoolExecutor 使用的任务队列 DelayQueue 封装了一个 PriorityQueue , PriorityQueue 会对队列中的任务进行排序 , 执行所需时间短的放在前面先被执行 , 如果执行所需时间相同则先提交的任务将被先执行 。 5. 徒手撸一个线程安全的 LRU 缓存
5.1. 实现方法
ConcurrentHashMap + ConcurrentLinkedQueue +ReadWriteLock
5.2. 原理
ConcurrentHashMap 是线程安全的Map , 我们可以利用它缓存 key,value形式的数据 。 ConcurrentLinkedQueue是一个线程安全的基于链表的队列(先进先出) , 我们可以用它来维护 key。 每当我们put/get(缓存被使用)元素的时候 , 我们就将这个元素对应的 key 存放在队列尾部 , 这样就能保证队列头部的元素是最近最少使用的 。 当我们的缓存容量不够的时候 , 我们直接移除队列头部对应的key以及这个key对应的缓存即可!
另外 , 我们用到了ReadWriteLock(读写锁)来保证线程安全 。
5.3. put方法具体流程分析
为了方便大家理解 , 我将代码中比较重要的 put(key,value)方法的原理图画了出来 , 如下图所示:
本文插图
5.4. 源码/** *@authorshuang.kou *
*使用ConcurrentHashMap+ConcurrentLinkedQueue+ReadWriteLock实现线程安全的LRU缓存 *这里只是为了学习使用 , 本地缓存推荐使用 Guava 自带的 。 使用Spring的话 , 推荐使用SpringCache */ publicclassMyLruCache
{ /** *缓存的最大容量 */ privatefinalintmaxCapacity; privateConcurrentHashMap
cacheMap; privateConcurrentLinkedQueuekeys; /** *读写锁 */ privateReadWriteLockreadWriteLock=newReentrantReadWriteLock(); privateLockwriteLock=readWriteLock.writeLock(); privateLockreadLock=readWriteLock.readLock(); publicMyLruCache(intmaxCapacity){ if(maxCapacity
(maxCapacity); keys=newConcurrentLinkedQueue<>(); } publicVput(Kkey,Vvalue){ //加写锁 writeLock.lock(); try{ //1.key是否存在于当前缓存 if(cacheMap.containsKey(key)){ moveToTailOfQueue(key); cacheMap.put(key,value); returnvalue; } //2.是否超出缓存容量 , 超出的话就移除队列头部的元素以及其对应的缓存 if(cacheMap.size()==maxCapacity){ System.out.println("maxCapacityofcachereached"); removeOldestKey(); } //3.key不存在于当前缓存 。 将key添加到队列的尾部并且缓存key及其对应的元素 keys.add(key); cacheMap.put(key,value); 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); } } publicintsize(){ returncacheMap.size(); } }
推荐阅读
- 互联网分析师于斌|对于陆正耀“背后”的愉悦资本来说,反思才是第一要务
- 互联网的一些事|QuestMobile:2020 移动互联网人均使用时长同比增长 12.9%
- 互联网的放大镜TB|vivo X50Pro发布,小米10瞬间不香了?
- |传统互联网产品经理正在消失,如何自救?
- |搜狐畅游的修仙往事:当互联网公司开始“拜神信教”
- 互联网乱侃先生|国产机们不用再看高通脸色?超级芯片巨头入场,GPU比高通强很多
- 基建新基建按下互联网保险“加速键” 慧择头部效应凸显
- 36氪|36氪独家|TCL旗下工业互联网平台「格创东智」获云锋基金亿元级A轮融资
- 互联网生活|不一般!世童科技品牌启动暨智能学习桌新品发布会惊艳中山
- 互联网乱侃秀|都说苹果没5G落后国产机了,结局却很让人扎心,销量又是第一
