Caffeine通过get()方法获取缓存中的数据。

Node<K, V> node = data.get(nodeFactory.newLookupKey(key));
if (node == null) {if (recordStats) {statsCounter().recordMisses(1);}return null;
}
long now = expirationTicker().read();
if (hasExpired(node, now)) {if (recordStats) {statsCounter().recordMisses(1);}scheduleDrainBuffers();return null;
}

首先将会通过neyLookupKey()构造用来搜索的key,如果是强引用的key那么直接就是调用时所传入的key,如果是弱引用的话将是一个继承自WeakReference的对象弱引用。

如果没有找到,记录到miss次数的统计中。

如果找到,在配置了过期时间的情况下,判断是否过期,如果过期也当做miss处理,并调用scheduleDrainBuffers()方法异步执行维护工作(在此处,如果异步维护任务在线程池中被拒绝,会直接同步执行过期的处理),整个同步取数据的流程结束返回null。

K castedKey = (K) key;
V value = node.getValue();if (!isComputingAsync(node)) {setVariableTime(node, expireAfterRead(node, castedKey, value, now));setAccessTime(node, now);
}
afterRead(node, now, recordStats);
return value;

如果找到并且没有过期,在当前该元素没有竞争的情况下,更新该元素的剩余过期时间(会在后面重新根据时间加入时间轮)和访问时间(会在后面重新加入到访问队列末尾或升级队列)。最后通过afterRead()准备进行异步清理操作和更新判断(可能会触发所选取数据的更新),并直接返回结果。

因此,在caffeine中一次同步获取元素的流程(在没有由于过期并只能同步执行维护方法的前提下),在耗时中,相比原始访问map,只多了一次过期时间判断和相关状态的更新。

void afterRead(Node<K, V> node, long now, boolean recordHit) {if (recordHit) {statsCounter().recordHits(1);}boolean delayable = skipReadBuffer() || (readBuffer.offer(node) != Buffer.FULL);if (shouldDrainBuffers(delayable)) {scheduleDrainBuffers();}refreshIfNeeded(node, now);
}

afterRead()中,首先在统计中记录命中操作,之后往readBuffer中添加针对该元素的read事件,这里的readBuffer会根据线程的threadlocal随机数选择线程专属的buffer,在写入事件中不会存在资源的竞争。(具体之前的文章有描述)

之后根据shouldDrainBuffers()方法判断,当前caffeine的异步维护任务是否正在执行,如果没有,则准备通过一开始提到的scheduleDrainBuffers()方法异步执行维护工作。

维护方法通过ForkJoinPool异步执行。

由元素访问而触发的维护方法在执行流程中主要分为6个主要过程(比write少一个add或者update的更新)。

drainReadBuffer();drainWriteBuffer();
if (task != null) {task.run();
}drainKeyReferences();
drainValueReferences();expireEntries();
evictEntries();

分别是获取所有readBuffer的read事件并执行相应的策略,获取writeBuffer(相比readBuffer只有一个)中的所有write事件执行,回收软弱引用的key和value,通过时间驱逐缓存中的元素,和根据lfu根据空间驱逐缓存中的元素。

在读操作中,主要关心readBuffer事件的处理。

void onAccess(Node<K, V> node) {if (evicts()) {K key = node.getKey();if (key == null) {return;}frequencySketch().increment(key);if (node.inEden()) {reorder(accessOrderEdenDeque(), node);} else if (node.inMainProbation()) {reorderProbation(node);} else {reorder(accessOrderProtectedDeque(), node);}} else if (expiresAfterAccess()) {reorder(accessOrderEdenDeque(), node);}if (expiresVariable()) {timerWheel().reschedule(node);}
}

在这里会异步更新该元素的lfu访问次数,并将该元素重新排到所在访问队列的末尾,如果是在probation队列中的元素,在足够的访问次数下,还会晋升到protect中,这部分在之前的文章有写。并根据该元素的剩余过期时间重新加入到时间轮中,时间轮的部分前面的文章也有写。

关于维护任务中的read部分暂且告一段落,回到之前 的afterRead()方法,会在最后调用refreshIfNeeded()方法,在这里会判断写入时间相比当前是否已经超出更新间隔,如果超出,将会通过异步方式重新执行元素的加载,重新加载获取新的值加入的缓存中。

重复之前的结论,在caffeine中一次同步获取元素的流程(在没有由于过期并维只能同步执行维护方法的前提下),在耗时中,相比原始访问map,只多了一次过期时间判断和相关状态的更新。

caffeine 读操作源码走读 为什么读这么快相关推荐

  1. 读Zepto源码之操作DOM

    2019独角兽企业重金招聘Python工程师标准>>> 这篇依然是跟 dom 相关的方法,侧重点是操作 dom 的方法. 读Zepto源码系列文章已经放到了github上,欢迎sta ...

  2. 从源码角度来读Handler

    最近打算从源码角度来读一下Handler,MessageQueue,Message,Looper,这四个面试必考项.所以今天先从Handler开始. 一.Handler的作用 源码定义 There a ...

  3. 试读angular源码第三章:初始化zone

    直接看人话总结 前言 承接上一章 项目地址 文章地址 angular 版本:8.0.0-rc.4 欢迎看看我的类angular框架 文章列表 试读angular源码第一章:开场与platformBro ...

  4. 读spring源码(一)-ClassPathXmlApplicationContext-初始化

    工作来几乎所有的项目都用到了spring,却一直没有系统的读下源码,从头开始系统的读下吧,分章也不那么明确,读到哪里记到哪里,仅仅作为个笔记吧. 先看ClassPathXmlApplicationCo ...

  5. 【读fastclick源码有感】彻底解决tap“点透”,提升移动端点击响应速度

    前言 近期使用tap事件为老夫带来了这样那样的问题,其中一个问题是解决了点透还需要将原来一个个click变为tap,这样的话我们就抛弃了ie用户 当然可以做兼容,但是没人想动老代码的,于是今天拿出了f ...

  6. java 事件分发机制_读Android源码之事件分发机制最全总结

    原标题:读Android源码之事件分发机制最全总结 本文源码来自andorid sdk 22,不同版本会有细微差别,但核心机制是一致的 一.概述 事件分发有多种类型, 本文主要介绍Touch相关的事件 ...

  7. 读Zepto源码之Deferred模块

    Deferred 模块也不是必备的模块,但是 ajax 模块中,要用到 promise 风格,必需引入 Deferred 模块.Deferred 也用到了上一篇文章<读Zepto源码之Callb ...

  8. 读Zepto源码之Ajax模块 1

    Ajax 模块也是经常会用到的模块,Ajax 模块中包含了 jsonp 的现实,和 XMLHttpRequest 的封装. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...

  9. 再读 ucosII源码(邵贝贝):内核结构

    本章给出μC/OS-Ⅱ的主要结构概貌: - μC/OS-Ⅱ是怎样处理临界段代码的; - 什么是任务,怎样把用户的任务交给μC/OS-Ⅱ; - 任务是怎样调度的; - 应用程序CPU的利用率是多少,μC ...

最新文章

  1. vue下轻松解决模拟微信视频缩略图拖拽→吸附窗口边界的功能
  2. ESB与可插拨系统的思考
  3. 1个多月就能看到效果的减肥大法 - 生活至上,美容至尚!
  4. 绵阳python培训_《绵》字意思读音、组词解释及笔画数 - 新华字典 - 911查询
  5. extjs5的grid垂直滚动条bug_Extjs grid panel 滚动条失效的解决方法
  6. html循环加载多个图片,两行代码实现图片碎片化加载
  7. jzoj3059-雕塑【容斥,数论】
  8. java ee 下载 安装配置_JavaEE下载安装及配置.doc
  9. 一步步编写操作系统 41 快表tlb 简介
  10. Kubernetes Ingress入门指南和实践练习
  11. 总结better-scroll插件的使用
  12. DzzOffice_flowplayer播放器更改
  13. 关于自动量程万用表和自动档位万用表使用
  14. 英文字母pc是什么意思,互联网的pc指的是什么
  15. Riverbed:广域网优化应对“云”发展
  16. 阿里云无法 git clone 的解决
  17. opencore 启动总是在win_黑苹果OpenCore引导总结
  18. 河北大学计算机类信息安全专业就业前景,2018信息安全专业就业前景和就业方向分析...
  19. 2017年最新App Store审核指南(官方)
  20. jupyterlab:Failed to load the jupyterlab-git server extension问题如何解决?

热门文章

  1. Mac安装prometheus node_exporter
  2. 字符串 读取西门子_【必学技能】自己动手——基于C#实现手机APP远程访问西门子PLC...
  3. 西北大学集训队选拔赛 F-三生三世(STL set和map的简单应用)
  4. ntop linux,linux下安装ntop
  5. python收取wss数据_大宗商品现货数据不好拿?商品季节性难跟踪?Python爬虫一键解决没烦恼...
  6. ASP.NET中密码保护,MD5和SHA1算法的使用
  7. ASP.NET MVC one view bind many model
  8. PostgreSQL 设置单条SQL的执行超时 - 防雪崩
  9. your port 80 is actually used by server IIS解决办法
  10. Centos系统普通用户开启sudo命令