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. ORA-01653:表无法通过64(在表空间USERS中)扩展

    问题描述:oracle插入数据时显示ORA-01653 表无法通过64(在表空间USERS中)扩展 原因:  oracle  表空间满了,需要扩展 截图: 解决方法: 1.首先查下表空间 select ...

  2. linux shell if语句

    #!/bin/bash read -p "please input Y/N" keyWord if [ "$keyWord" == "Y" ...

  3. Win7 64 bit 激活工具

    下载地址:http://www.3987.com/xiazai/1/18/35487.html 一键运行,重启就激活了,简洁方便!

  4. 数据挖掘之clara算法原理及实例(代码中有bug)

    继上两篇文章介绍聚类中基于划分思想的k-means算法和k-mediod算法 本文将继续介绍另外一种基于划分思想的k-mediod算法-----clara算法 clara算法可以说是对k-mediod ...

  5. 【HDU 4771 Stealing Harry Potter's Precious】BFS+状压

    2013杭州区域赛现场赛二水... 类似“胜利大逃亡”的搜索问题,有若干个宝藏分布在不同位置,问从起点遍历过所有k个宝藏的最短时间. 思路就是,从起点出发,搜索到最近的一个宝藏,然后以这个位置为起点, ...

  6. 【POJ 2823 Sliding Window】 单调队列

    题目大意:给n个数,一个长度为k(k<n)的闭区间从0滑动到n,求滑动中区间的最大值序列和最小值序列. 最大值和最小值是类似的,在此以最大值为例分析. 数据结构要求:能保存最多k个元素,快速取得 ...

  7. 杭电oj 2037 今年暑假不AC

    Tips:贪心算法的典型应用,可以按照节目结束时间由小到大排序,(至于结束时间相同的,有些人说按开始时间早的排序,不过个人认为不必处理,因为结束时间一样,两个之中要么都没有,要么必有一个)然后再依次进 ...

  8. word2vec 入门基础(一)

    一.基本概念 word2vec是Google在2013年开源的一个工具,核心思想是将词表征映 射为对应的实数向量. 目前采用的模型有一下两种 CBOW(Continuous Bag-Of-Words, ...

  9. cocos2d-x ios游戏开发初认识(八) 触摸事件与碰撞检測

    玩过植物大战僵尸都知道,要在草坪里放一朵向日葵或者其他的植物仅仅需触摸那个植物将其拖入到想要摆放的位置,这事实上就是这节要写的触摸事件.还能够发现当我们的僵尸出来的时候,我们的小豌豆会发子弹攻击僵尸, ...

  10. JAVA Socket无参构造方法的使用

    1.Socket类的构造方法很多,只有无参构造方法不会尝试建立连接,其他构造方法,都会尝试建立连接的,如果建立连接失败,将会抛出异常.如果想为Socket设定连接超时时间,此时就需要使用无参构造方法, ...