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;
    虽然这确实会带来一定的空间消耗,但缓存器本身对性能要求很高,以空间换时间是可以接受的。
  • 相关阅读:
    215. Kth Largest Element in an Array
    214. Shortest Palindrome
    213. House Robber II
    212. Word Search II
    210 Course ScheduleII
    209. Minimum Size Subarray Sum
    208. Implement Trie (Prefix Tree)
    207. Course Schedule
    206. Reverse Linked List
    sql 开发经验
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3329205.html
Copyright © 2011-2022 走看看