此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

下面介绍在LocalCache(CacheBuilder, CacheLoader)中调用的一些方法:

  • CacheBuilder-->getConcurrencyLevel()

    int getConcurrencyLevel() {
            return (concurrencyLevel == UNSET_INT) ? //是否设置了concurrencyLevel
                    DEFAULT_CONCURRENCY_LEVEL//如果没有设置,采用默认值16
                    : concurrencyLevel;//如果设置了,采用设置的值
        }

    说明:检查是否设置了concurrencyLevel,如果设置了,采用设置的值,如果没有设置,采用默认值16

  • CacheBuilder-->getKeyStrength()

    //获取键key的强度(默认为Strong,还有weak和soft)
        Strength getKeyStrength() {
            return MoreObjects.firstNonNull(keyStrength, Strength.STRONG);
        }

    说明:获取key的引用类型(强度),默认为Strong(强引用类型),下表列出MoreObjects的方法firstNonNull(@Nullable T first, @Nullable T second)

    public static <T> T firstNonNull(@Nullable T first, @Nullable T second) {
        return first != null ? first : checkNotNull(second);
      }
  • CacheBuilder-->getValueStrength()

        Strength getValueStrength() {
            return MoreObjects.firstNonNull(valueStrength, Strength.STRONG);
        }

    说明:获取value的引用类型(强度),默认为Strong(强引用类型)

  • CacheBuilder-->getExpireAfterWriteNanos()

    long getExpireAfterWriteNanos() {
            return (expireAfterWriteNanos == UNSET_INT) ? 
                    DEFAULT_EXPIRATION_NANOS
                    : expireAfterWriteNanos;
        }

    说明:获取超时时间,如果设置了,就是设置值,如果没设置,默认是0

  • CacheBuilder-->getInitialCapacity()

    int getInitialCapacity() {
            return (initialCapacity == UNSET_INT) ? 
                    DEFAULT_INITIAL_CAPACITY
                    : initialCapacity;
        }

    说明:获取初始化容量,如果指定了就是用指定容量,如果没指定,默认为16。值得注意的是,该容量是用于计算每个Segment的容量的,并不一定是每个Segment的容量,其具体使用的方法见LocalCache(CacheBuilder, CacheLoader)

  • LocalCache-->evictsBySize()

    //这里maxWeight没有设置值,默认为UNSET_INT,即-1
    
        boolean evictsBySize() {
            return maxWeight >= 0;
        }

    说明:这是一个与weight相关的方法,由于我们没有设置weight,所以该方法对我们的程序没有影响。

  • EntryFactory-->getFatory()

    /**
             * Masks used to compute indices in the following table.
             */
            static final int ACCESS_MASK = 1;
            static final int WRITE_MASK = 2;
            static final int WEAK_MASK = 4;         /**
             * Look-up table for factories.
             */
            static final EntryFactory[] factories = { STRONG, STRONG_ACCESS,
                    STRONG_WRITE, STRONG_ACCESS_WRITE, WEAK, WEAK_ACCESS,
                    WEAK_WRITE, WEAK_ACCESS_WRITE, };         static EntryFactory getFactory(Strength keyStrength,
                                           boolean usesAccessQueue, 
                                           boolean usesWriteQueue) {
                int flags = ((keyStrength == Strength.WEAK) ? WEAK_MASK : 0)//0
                        | (usesAccessQueue ? ACCESS_MASK : 0)//0
                        | (usesWriteQueue ? WRITE_MASK : 0);//WRITE_MASK-->2
                return factories[flags];//STRONG_WRITE
            }

    说明:EntryFactory是LocalCache的一个内部枚举类,通过上述方法,获取除了相应的EntryFactory,这里选出的是STRONG_WRITE工厂,该工厂代码如下:

            STRONG_WRITE {
                /**
                 * 创建新的Entry
                 */
                @Override
                <K, V> ReferenceEntry<K, V> newEntry(Segment<K, V> segment, 
                                                     K key,
                                                     int hash, 
                                                     @Nullable ReferenceEntry<K, V> next) {
                    return new StrongWriteEntry<K, V>(key, hash, next);
                }             /**
                 * 将原来的Entry(original)拷贝到当下的Entry(newNext)
                 */
                @Override
                <K, V> ReferenceEntry<K, V> copyEntry(Segment<K, V> segment,
                                                      ReferenceEntry<K, V> original, 
                                                      ReferenceEntry<K, V> newNext) {
                    ReferenceEntry<K, V> newEntry = super.copyEntry(segment,
                            original, newNext);
                    copyWriteEntry(original, newEntry);
                    return newEntry;
                }
            }

    在该工厂中,指定了创建新entry的方法与复制原有entry为另一个entry的方法。

  • LocalCache-->newSegmentArray(int ssize)

    /**
         * 创建一个指定大小的Segment数组
         */
        @SuppressWarnings("unchecked")
        final Segment<K, V>[] newSegmentArray(int ssize) {
            return new Segment[ssize];
        }

    说明:该方法用于创建一个指定大小的Segment数组。关于Segment的介绍后边会说。

  • LocalCache-->createSegment(initialCapacity,maxSegmentWeight,StatsCounter)

        Segment<K, V> createSegment(int initialCapacity, 
                                    long maxSegmentWeight,
                                    StatsCounter statsCounter) {
            return new Segment<K, V>(this, 
                                     initialCapacity, 
                                     maxSegmentWeight,
                                     statsCounter);
        }

    该方法用于为之前创建的Segment数组的每一个元素赋值。

    下边列出Segment类的一些属性和方法:

    final LocalCache<K, V> map;// 外部类的一个实例
    
            /** 该Segment中已经存在缓存的个数  */
            volatile int count;         /**
             * 指定是下边的AtomicReferenceArray<ReferenceEntry<K, V>> table,即扩容也是只扩自己的Segment
             * The table is expanded when its size exceeds this threshold. (The
             * value of this field is always {@code (int) (capacity * 0.75)}.)
             */
            int threshold;         /**
             * 每个Segment中的table
             */
            volatile AtomicReferenceArray<ReferenceEntry<K, V>> table;         /**
             * The maximum weight of this segment. UNSET_INT if there is no maximum.
             */
            final long maxSegmentWeight;         /**
             * map中当前元素的一个队列,队列元素根据write time进行排序,每write一个元素就将该元素加在队列尾部
             */
            @GuardedBy("this")
            final Queue<ReferenceEntry<K, V>> writeQueue;         /**
             * A queue of elements currently in the map, ordered by access time.
             * Elements are added to the tail of the queue on access (note that
             * writes count as accesses).
             */
            @GuardedBy("this")
            final Queue<ReferenceEntry<K, V>> accessQueue;         Segment(LocalCache<K, V> map, int initialCapacity,
                    long maxSegmentWeight, StatsCounter statsCounter) {
                this.map = map;
                this.maxSegmentWeight = maxSegmentWeight;//0
                this.statsCounter = checkNotNull(statsCounter);
                initTable(newEntryArray(initialCapacity));             writeQueue = map.usesWriteQueue() ? //过期时间>0
                             new WriteQueue<K, V>() //WriteQueue
                             : LocalCache.<ReferenceEntry<K, V>> discardingQueue();             accessQueue = map.usesAccessQueue() ? //false
                              new AccessQueue<K, V>()
                              : LocalCache.<ReferenceEntry<K, V>> discardingQueue();
            }         AtomicReferenceArray<ReferenceEntry<K, V>> newEntryArray(int size) {
                return new AtomicReferenceArray<ReferenceEntry<K, V>>(size);//new Object[size];
            }         void initTable(AtomicReferenceArray<ReferenceEntry<K, V>> newTable) {
                this.threshold = newTable.length() * 3 / 4; // 0.75
                if (!map.customWeigher() && this.threshold == maxSegmentWeight) {
                    // prevent spurious expansion before eviction
                    this.threshold++;
                }
                this.table = newTable;
            }

    Segment的构造器完成了三件事儿:为变量复制 + 初始化Segment的table + 构建相关队列

    • initTable(newEntryArray(initialCapacity))源代码在Segment类中已给出:初始化table的步骤简述为:创建一个指定个数的ReferenceEntry数组,计算扩容值。

    • 其他队列不说了,这里实际上只用到了WriteQueue,建立该Queue的目的是用于实现LRU缓存回收算法

到目前为止,guava cache的完整的一个数据结构基本上就建立起来了。最后再总结一下。

guava cache的数据结构:

guava cache的数据结构的构建流程:

1)构建CacheBuilder实例cacheBuilder

2)cacheBuilder实例指定缓存器LocalCache的初始化参数

3)cacheBuilder实例使用build()方法创建LocalCache实例(简单说成这样,实际上复杂一些)

3.1)首先为各个类变量赋值(通过第二步中cacheBuilder指定的初始化参数以及原本就定义好的一堆常量)

3.2)之后创建Segment数组

3.3)最后初始化每一个Segment[i]

3.3.1)为Segment属性赋值

3.3.2)初始化Segment中的table,即一个ReferenceEntry数组(每一个key-value就是一个ReferenceEntry)

3.3.3)根据之前类变量的赋值情况,创建相应队列,用于LRU缓存回收算法

这里,我们就用开头给出的代码实例,来看一下,最后构建出来的cache结构是个啥:

显示指定:

expireAfterWriteNanos==20min   maximumSize==1000

默认值:

concurrency_level==4(用于计算Segment个数)     initial_capcity==16 (用于计算每个Segment容量)

keyStrength==STRONG    valueStrength==STRONG

计算出:

entryFactory==STRONG_WRITE

segmentCount==4:Segment个数,一个刚刚大于等于concurrency_level且是2的几次方的一个数

segmentCapacity==initial_capcity/segmentCount==4:用来计算每个Segment能放置的entry个数的一个值,一个刚刚等于initial_capcity/segmentCount或者比initial_capcity/segmentCount大1的数(关键看是否除尽)

segmentSize==4:每个Segment能放置的entry个数,刚刚>=segmentCapacity&&是2的几次方的数

segments==Segment[segmentCount]==Segment[4]

segments[i]:

  • 包含一个ReferenceEntry[segmentSize]==ReferenceEntry[4]

  • WriteQueue:用于LRU算法的队列

  • threshold==newTable.length()*3/4==segmentSize*3/4==3:每个Segment中有了3个Entry(key-value),就会扩容,扩容机制以后在添加Entry的时候再讲

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 JVM内存回收区域+对象存活的判断+引用类型+垃圾回收线程
【推荐】 JVM锁实现探究2:synchronized深探

Google guava cache源码解析1--构建缓存器(3)的更多相关文章

  1. Google guava cache源码解析1--构建缓存器(1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHas ...

  2. 第二章 Google guava cache源码解析1--构建缓存器

    1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHashMap(或者说成就是一个ConcurrentHashMap,只是在其上多添加了一些功能) ...

  3. Google guava cache源码解析1--构建缓存器(2)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. CacheBuilder-->maximumSize(long size)     /**       ...

  4. Guava Cache源码解析

    概述: 本次主要是分析cache的源码,基本概念官方简介即可. 基本类图: 在官方的文档说明中,Guava Cache实现了三种加载缓存的方式: LoadingCache在构建缓存的时候,使用buil ...

  5. [源码解析] PyTorch分布式优化器(1)----基石篇

    [源码解析] PyTorch分布式优化器(1)----基石篇 目录 [源码解析] PyTorch分布式优化器(1)----基石篇 0x00 摘要 0x01 从问题出发 1.1 示例 1.2 问题点 0 ...

  6. [源码解析] PyTorch分布式优化器(2)----数据并行优化器

    [源码解析] PyTorch分布式优化器(2)----数据并行优化器 目录 [源码解析] PyTorch分布式优化器(2)----数据并行优化器 0x00 摘要 0x01 前文回顾 0x02 DP 之 ...

  7. [源码解析] PyTorch分布式优化器(3)---- 模型并行

    [源码解析] PyTorch分布式优化器(3)---- 模型并行 目录 [源码解析] PyTorch分布式优化器(3)---- 模型并行 0x00 摘要 0x01 前文回顾 0x02 单机模型 2.1 ...

  8. Guava Cache源码详解

    目录 一.引子 二.使用方法 2.1 CacheBuilder有3种失效重载模式 2.2 测试验证 三.源码剖析 3.1 简介 3.2 源码剖析 四.总结 优点: 缺点: 正文 回到顶部 一.引子 缓 ...

  9. 常用限流算法与Guava RateLimiter源码解析

    在分布式系统中,应对高并发访问时,缓存.限流.降级是保护系统正常运行的常用方法.当请求量突发暴涨时,如果不加以限制访问,则可能导致整个系统崩溃,服务不可用.同时有一些业务场景,比如短信验证码,或者其它 ...

随机推荐

  1. 字符串匹配 - sunday算法

    常见的字符串匹配算法有BF.KMP(教科书中非常经典的).BM.Sunday算法 这里主要想介绍下性能比较好并且实现比较简单的Sunday算法 . 基本原理: 从前往后匹配,如果遇到不匹配情况判断母串 ...

  2. 不立flag了……

    当天刚说再也不想下这游戏了,后来和女友聊了会天视了会屏又动摇了..后悔和她那么计较这些小事,可能玩游戏时生气时就想不起来那么多事了吧..于是游戏过两天就又下回来了.. 这两天培训课程也是很快的感觉,昨 ...

  3. MySQL Server参数优化 - innodb_file_per_table(独立表空间)

    1 简介    Innodb存储引擎可将所有数据存放于ibdata*的共享表空间,也可将每张表存放于独立的.ibd文件的独立表空间.    共享表空间以及独立表空间都是针对数据的存储方式而言的.    ...

  4. 核心一:DI

    1.DI:中文名称:依赖注入 2.英文名称:(Dependency Injection) 3.DI是什么?? 3.1 DI和IoC是一样的 3.2 当一个类(A)中需要依赖另一类(B)对象时,把B赋值 ...

  5. 2019.01.19 bzoj4592: [Shoi2015]脑洞治疗仪(ODT)

    传送门 ODT水题. 支持区间01赋值,区间填补(把区间[l,r][l,r][l,r]从左往右数kkk个1都变成0),区间查询最长连续1个数. 思路: 区间填补操作感觉不是很好弄,写线段树的神仙可以套 ...

  6. 使用express框架和mongoose在MongoDB新增数据

    1.安装 express npm i express --save 2.安装 mongoDB npm i mongodb --save 3.安装mongoose npm i mongoose --sa ...

  7. 根据方法名获取方法Body Content

    利用 MethodBody类的GetILAsByteArray方法可以获取到返回字节数组的MSIL的body.然后再去解析此字节数组, 可以得到MSIL,然后你再去解析MSIL,你就可以得到你想到so ...

  8. 好文推荐系列--------(3)GruntJS 在线重载 提升生产率至新境界

    好文原文地址:http://segmentfault.com/a/1190000000354555 本文将首先介绍grunt-markdown插件如何配合HTML模板使用,接着我将介绍如何使用grun ...

  9. 【王者荣耀之IT大神版】1.1版本升级之“投降机制”

    版本:1.1 关于“投降机制”的理论基础与灵感来源于<微习惯>这本书. 简单来说,微习惯就是很小很小的习惯,比如说,每天做一个俯卧撑,每天看一页书等等.我们以前也许有过很多的计划,但却总是 ...

  10. Ubuntu下安装并配置TexStudio

    作者:tongqingliu 转载请注明出处:http://www.cnblogs.com/liutongqing/p/7069715.html Ubuntu下安装并配置TexStudio Ubunt ...