zoukankan      html  css  js  c++  java
  • Guava缓存器源码分析——缓存统计器

    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;
    虽然这确实会带来一定的空间消耗,但缓存器本身对性能要求很高,以空间换时间是可以接受的。
  • 相关阅读:
    [LintCode] Set Matrix Zeros
    [LintCode] Identify Celebrity
    [LintCode] Edit Distance
    [LintCode] Edit Distance II
    [LintCode] Strings Serialization
    二十七. Keepalived热备 Keepalived+LVS 、 HAProxy服务器
    二十六. 集群及LVS简介 LVS-NAT集群 LVS-DR集群
    二十五 存储技术与应用 iSCSI技术应用 、 udev配置 NFS网络文件系统 、 Multipath多路径 、 NFS网络文件系统 、 udev配置
    二十三.Subversion基本操作、使用Subversion协同工作、制作nginx的RPM包
    二十二. 安装部署Tomcat服务器、使用Tomcat部署虚拟主机、使用Varnish加速Web
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3329205.html
Copyright © 2011-2022 走看看