Guava缓存器统计器实现:
全局统计器——

        1、CacheBuilder的静态成员变量Supplier<StatsCounter> CACHE_STATS_COUNTER初始化时,重载的get方法,返回了一个SimpleStatsCounter实例。
        2、当缓存器开启缓存统计时(recordStats),其成员变量statsCounterSupplier被赋值为CACHE_STATS_COUNTER,若没开启则为初始值NULL_STATS_COUNTER。
 3、在LocalCache的构造函数中,缓存器的全局统计器globalStatsCounter将从CacheBuilder中获取:builder.getStatsCounterSupplier().get();
因此Guava缓存器的全局统计器实际上是SimpleStatsCounter类型。
       全局统计器只有在调用getAll, getAllPresent, getIfPresent, loadAll方法时才会更新。

段统计器——

缓存器的每个段都有自己的统计器statsCounter,在LocalCache的构造函数中,通过createSegment方法创建所有的段,同时通过 builder.getStatsCounterSupplier().get()完成对段统计器的初始化,因此段统计器也是SimpleStatsCounter类型。

当用户想查看缓存统计信息时,会调用stats方法,将全局统计器及每一个段统计器的信息综合起来:
       public CacheStats stats() {
              SimpleStatsCounter aggregator = new SimpleStatsCounter();
              aggregator.incrementBy(localCache.globalStatsCounter);
              for (Segment<K, V> segment : localCache.segments) {
                    aggregator.incrementBy(segment.statsCounter);
              }
              return aggregator.snapshot();
        }
        最终通过snapshot方法,返回包含所有统计信息的CacheStats对象:
        public CacheStats snapshot() {
              return new CacheStats(
              hitCount.sum(),
              missCount.sum(),
              loadSuccessCount.sum(),
              loadExceptionCount.sum(),
              totalLoadTime.sum(),
              evictionCount.sum());
        }



SimpleStatsCounter中所有的LongAddable类型成员变量,都是通过
LongAddables的create方法初始化,该静态方法实际调用的为 SUPPLIER.get()方法,返回一个
LongAddable实例。

        在LongAddables 中有一段静态代码段,完成了对其成员变量Supplier<LongAddable> SUPPLIER的初始化,并重载了Supplier的get方法,该get方法返回一个LongAdder对象,如果初始化出现异常,则重新初始化SUPPLIER,但是重载的get方法中,返回一个PureJavaLongAddable对象。

1、PureJavaLongAddable继承至AtomicLong,AtomicLong使用原子方法实现了对一个Long对象的增、减、更新等操作。 比如对于++运算符 AtomicLong 可以将它持有的 Long对象原子地递增。 PureJavaLongAddable中的方法都通过AtomicLong来实现,以保证所有操作都能原子地完成,比如add方法实际调用的即为AtomicLong.getAndAdd,该方法将当前值加上一个数,并返回原值:
       public final long getAndAdd(long delta) {
            while (true) {
                    long current = get();
                    long next = current + delta;
                    if (compareAndSet(current, next))
                            return current;
            }
        }

2、按Guava的说法,当多条线程在更新统计数据时,而不是细粒度同步控制的情况下,LongAdder比AtomicLong更好用。当更新争用的频率低时,两个类效果比较相似,当争用频率很高时,LongAdder的吞吐率将会大大提升,但会消耗更大的空间。
        下面分析下LongAdder的add方法——
        其中cells和base都为Striped64的成员变量,cells为数组类型,base作为一个保底值,当不发生争用时更新它。
        public void add(long x) {
                Cell[] as; long b, v; HashCode hc; Cell a; int n;
                //当cells不为空,或对base值更新失败时,进入分支;

                if ((as = 
cells) != null || !casBase(b = 
base, b + x)) {
                        boolean uncontended = true;
                        //获取hash值;
                        int h = (hc = threadHashCode.get()).code; 
                        //当cells为空,或其长度小于1,或从cells中随机取的cell为空,或对随机所取得cell更新失败时(发生争用),则进入分支,重新更新,若发生争用,此时uncontended为false,在重新更新时会使用到busy锁;
                        if (as == null || (n = as.length) < 1 ||(a = as[(n - 1) & h]) == null || !(uncontended = a.cas(v = a.value, v + x)))
                                retryUpdate(x, hc, uncontended);
                }
        }

因此,LongAdder主要是通过Cell[] cells,将同步操作,转嫁到随机取得的cells元素上,从而使得争用的概率大大降低,同时在发生争用时,retryUpdate方法中还可能会对cells数据进行扩容,以降低争用的发生:
        Cell[] rs = new Cell[n << 1];

        for (int i = 0; i < n; ++i)
                rs[i] = as[i];
        
cells = rs;
虽然这确实会带来一定的空间消耗,但缓存器本身对性能要求很高,以空间换时间是可以接受的。

Guava缓存器源码分析——缓存统计器的更多相关文章

  1. Guava缓存器源码分析——删除消息

    Guava缓存器的删除消息机制 测试代码——             LoadingCache<String, Integer> cache = CacheBuilder.newBuild ...

  2. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  3. Linux 内核调度器源码分析 - 初始化

    导语 上篇系列文 混部之殇-论云原生资源隔离技术之CPU隔离(一) 介绍了云原生混部场景中CPU资源隔离核心技术:内核调度器,本系列文章<Linux内核调度器源码分析>将从源码的角度剖析内 ...

  4. linux调度器源码分析 - 运行(四)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 之前的文章已经将调度器的数据结构.初始化.加入进程都进行了分析,这篇文章将主要说明调度器是如何在程序稳定运 ...

  5. linux调度器源码分析 - 初始化(二)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 上期文章linux调度器源码分析 - 概述(一)已经把调度器相关的数据结构介绍了一遍,本篇着重通过代码说明 ...

  6. 一步步实现windows版ijkplayer系列文章之三——Ijkplayer播放器源码分析之音视频输出——音频篇

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  7. 一步步实现windows版ijkplayer系列文章之二——Ijkplayer播放器源码分析之音视频输出——视频篇

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  8. Golang package轻量级KV数据缓存——go-cache源码分析

    作者:Moon-Light-Dream 出处:https://www.cnblogs.com/Moon-Light-Dream/ 转载:欢迎转载,但未经作者同意,必须保留此段声明:必须在文章中给出原文 ...

  9. OkHttp3 拦截器源码分析

    OkHttp 拦截器流程源码分析 在这篇博客 OkHttp3 拦截器(Interceptor) ,我们已经介绍了拦截器的作用,拦截器是 OkHttp 提供的对 Http 请求和响应进行统一处理的强大机 ...

随机推荐

  1. alsa音频驱动科普第一课

    做linux音频编程对alsa应该不陌生. 但是对于刚接触这块技术的同学来说是一件困难的事情.原因在于:网上关于alsa的资料太少了,特别国内的资料更是大部分重复.对于初学者来说特别苦恼. 由于笔者经 ...

  2. python bool值要注意的一些地方

    1.像(),[],{}这三个是可以通过bool(()),bool([]),bool({})转化为bool值的:且它们转化后的结果为False.但是这三个值它本身并不等于False.切记不可以与Fals ...

  3. Azure File SMB3.0文件共享服务(4)

    在Linux上使用Azure文件共享服务 使用SMB 3.0从用户自己的数据连接到Azure,需要加密连接,但目前的Linux SMB客户端都暂时都不支持,Linux的开源社区正在努力将该功能添加到L ...

  4. mh

    http://video.sina.com.cn/vlist/news/zt/mlxyhkhbsl/#131455718 http://www.cnblogs.com/xinye/archive/20 ...

  5. js的框架

    Ember.js的一些学习总结   1.1.1 摘要 现在,我们经常都可以看到复杂的JavaScript应用程序,由于这些应用程序变得越来越复杂,一长串的jQuery回调语句或者通过应用程序在各个状态 ...

  6. WAS

    http://pic.dhe.ibm.com/infocenter/wasinfo/v8r0/index.jsp?topic=%2Fcom.ibm.websphere.express.doc%2Fin ...

  7. 电子科大POJ "任意阶矩阵相乘"

    任意阶矩阵的乘法 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) C-sourc ...

  8. android笔记1——开发环境的搭建

    Long Long ago...已经成为了历史,我还是要说出一个真相:早年前,那时候,android还不被大众所认知的时候,当然开发人员也没不像如今那样趋于饱和状态.一位大牛前辈,也是我的学长,那时候 ...

  9. HTML5开发 BUG解决

    1.点透Q:元素A上定位另外一个元素B,点击元素B,如果元素A有事件或链接,会触发元素A上的事件或链接,即点透A:在元素B的touchend中增加ev.preventDefault();阻止默认事件即 ...

  10. 【27前端】base标签带有href属性会让chrome里的svg元素url失效

    一个chrome的问题,但具体原因不明. 触发条件:chrome浏览器base标签里href属性有值的时候 触发问题:svg里面的元素如果有用url的滤镜和模糊,则会失效,在firefox里和IE10 ...