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

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

CacheBuilder-->maximumSize(long size)

    /**
     * 指定cache中最多能存放的entry(key-value)个数maximumSize
     * 注意:
     * 1、在entry个数还未达到这个指定个数maximumSize的时候,可能就会发生缓存回收
     * 上边这种情况发生在cache size接近指定个数maximumSize,
     * cache就会回收那些很少会再被用到的缓存(这些缓存会使最近没有被用到或很少用到的),其实说白了就是LRU算法回收缓存
     * 2、maximumSize与maximumWeight不能一起使用,其实后者也很少会使用
     */
    public CacheBuilder<K, V> maximumSize(long size) {
        /* 检查maximumSize是否已经被设置过了 */
        checkState(this.maximumSize == UNSET_INT,
                   "maximum size was already set to %s", 
                   this.maximumSize);
        /* 检查maximumWeight是否已经被设置过了(这就是上边说的第二条)*/
        checkState(this.maximumWeight == UNSET_INT,
                   "maximum weight was already set to %s", 
                   this.maximumWeight);
        /* 这是与maximumWeight配合的一个属性 */
        checkState(this.weigher == null,
                   "maximum size can not be combined with weigher");
        /* 检查设置的maximumSize是不是>=0,通常不会设置为0,否则不会起到缓存作用 */
        checkArgument(size >= 0, "maximum size must not be negative");
        this.maximumSize = size;
        return this;
    }

注意:

  • 设置整个cache(而非每个Segment)中最多可存放的entry的个数

CacheBuilder-->build(CacheLoader<? super K1, V1> loader)

    /**
     * 建立一个cache,该缓存器通过使用传入的CacheLoader,
     * 既可以获取已给定key的value,也能够自动的计算和获取缓存(这说的就是get(Object key)的三步原子操作)
     * 当然,这里是线程安全的,线程安全的运行方式与ConcurrentHashMap一致
     */
    public <K1 extends K, V1 extends V> LoadingCache<K1, V1> build(CacheLoader<? super K1, V1> loader) {
        checkWeightWithWeigher();
        return new LocalCache.LocalLoadingCache<K1, V1>(this, loader);
    }

注意:

  • 要看懂该方法,需要了解一些泛型方法的使用方式与泛型限界

  • 该方法的返回值是一个LoadingCache接口的实现类LocalLoadingCache实例

  • 在build方法需要传入一个CacheLoader的实例,实际使用中使用了匿名内部类来实现的,源码的话,就是一个无参构造器,什么也没做,传入CacheLoader实例的意义就是"类结构"部分所说的load()方法

在上边调用build时,整个代码的执行权其实就交给了LocalCache.

3.2、LocalCache

LocalLoadingCahe构造器

    static class LocalLoadingCache<K, V> extends LocalManualCache<K, V>
                                         implements LoadingCache<K, V> {         LocalLoadingCache(CacheBuilder<? super K, ? super V> builder,
                          CacheLoader<? super K, V> loader) {
            super(new LocalCache<K, V>(builder, checkNotNull(loader)));
        }

说明:在该内部类的无参构造器的调用中,

1)首先要保证传入的CacheLoader实例非空,

2)其次创建了一个LocalCache的实例出来,

3)最后调用父类LocalManualCache的私有构造器将第二步创建出来的LocalCache实例赋给LocalCache的类变量,完成初始化。

这里最重要的就是第二步,下面着重讲第二步:

LocalCache的一些属性

    /** 最大容量(2的30次方),即最多可存放2的30次方个entry(key-value) */
    static final int MAXIMUM_CAPACITY = 1 << 30;     /** 最多多少个Segment(2的16次方)*/
    static final int MAX_SEGMENTS = 1 << 16;     /** 用于选择Segment */
    final int segmentMask;     /** 用于选择Segment,尽量将hash打散 */
    final int segmentShift;     /** 底层数据结构,就是一个Segment数组,而每一个Segment就是一个hashtable */
    final Segment<K, V>[] segments;     /** 
     * 并发水平,这是一个用于计算Segment个数的一个数,
     * Segment个数是一个刚刚大于或等于concurrencyLevel的数
     */
    final int concurrencyLevel;     /** 键的引用类型(strong、weak、soft) */
    final Strength keyStrength;     /** 值的引用类型(strong、weak、soft) */
    final Strength valueStrength;     /** The maximum weight of this map. UNSET_INT if there is no maximum. 
     * 如果没有设置,就是-1
     */
    final long maxWeight;     final long expireAfterAccessNanos;     final long expireAfterWriteNanos;     /** Factory used to create new entries. */
    final EntryFactory entryFactory;     /** 默认的缓存加载器,用于做一些缓存加载操作(其实就是load),实现三步原子操作*/
    @Nullable
    final CacheLoader<? super K, V> defaultLoader;     /** 默认的缓存加载器,用于做一些缓存加载操作(其实就是load),实现三步原子操作*/
    @Nullable
    final CacheLoader<? super K, V> defaultLoader;

说明:关于这些属性的含义,看注释+CacheBuilder部分的属性注释+ConcurrentHashMap的属性注释

LocalCache-->LocalCache(CacheBuilder, CacheLoader)

    /**
     * 创建一个新的、空的map(并且指定策略、初始化容量和并发水平)
     */
    LocalCache(CacheBuilder<? super K, ? super V> builder,
               @Nullable CacheLoader<? super K, V> loader) {
        /*
         * 默认并发水平是4,即四个Segment(但要注意concurrencyLevel不一定等于Segment个数)
         * Segment个数:一个刚刚大于或等于concurrencyLevel且是2的几次方的一个数
         */
        concurrencyLevel = Math
                .min(builder.getConcurrencyLevel(), MAX_SEGMENTS);         keyStrength = builder.getKeyStrength();//默认为Strong,即强引用
        valueStrength = builder.getValueStrength();//默认为Strong,即强引用         // 缓存超时(时间起点:entry的创建或替换(即修改))
        expireAfterWriteNanos = builder.getExpireAfterWriteNanos();
        // 缓存超时(时间起点:entry的创建或替换(即修改)或最后一次访问)
        expireAfterAccessNanos = builder.getExpireAfterAccessNanos();
        //创建entry的工厂
        entryFactory = EntryFactory.getFactory(keyStrength,
                                                  usesAccessEntries(), 
                                                  usesWriteEntries());
        //默认的缓存加载器
        defaultLoader = loader;         // 初始化容量为16,整个cache可以放16个缓存entry
        int initialCapacity = Math.min(builder.getInitialCapacity(),
                                       MAXIMUM_CAPACITY);         int segmentShift = 0;
        int segmentCount = 1;
        //循环条件的&&后边的内容是关于weight的,由于没有设置maxWeight,所以其值为-1-->evictsBySize()返回false
        while (segmentCount < concurrencyLevel
                && (!evictsBySize() || segmentCount * 20 <= maxWeight)) {
            ++segmentShift;
            segmentCount <<= 1;//找一个刚刚大于或等于concurrencyLevel的Segment数
        }
        this.segmentShift = 32 - segmentShift;
        segmentMask = segmentCount - 1;         this.segments = newSegmentArray(segmentCount);//创建指定大小的数组         int segmentCapacity = initialCapacity / segmentCount;//计算每一个Segment中的容量的值,刚刚大于等于initialCapacity/segmentCount
        if (segmentCapacity * segmentCount < initialCapacity) {
            ++segmentCapacity;
        }         int segmentSize = 1;//每一个Segment的容量
        while (segmentSize < segmentCapacity) {
            segmentSize <<= 1;//刚刚>=segmentCapacity&&是2的几次方的数
        }         if (evictsBySize()) {//由于没有设置maxWeight,所以其值为-1-->evictsBySize()返回false
            // Ensure sum of segment max weights = overall max weights
            long maxSegmentWeight = maxWeight / segmentCount + 1;
            long remainder = maxWeight % segmentCount;
            for (int i = 0; i < this.segments.length; ++i) {
                if (i == remainder) {
                    maxSegmentWeight--;
                }
                this.segments[i] = createSegment(segmentSize, 
                                                 maxSegmentWeight,
                                                 builder.getStatsCounterSupplier().get());
            }
        } else {
            for (int i = 0; i < this.segments.length; ++i) {
                this.segments[i] = createSegment(segmentSize, 
                                                 UNSET_INT,
                                                 builder.getStatsCounterSupplier().get());
            }
        }
    }

说明:这里的代码就是整个LocalCache实例的创建过程,非常重要!!!


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

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

相关文章:
【推荐】 流式断言器AssertJ介绍

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

  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--构建缓存器(3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 下面介绍在LocalCache(CacheBuilder, CacheLoader)中调用的一些方法: Ca ...

  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. javascript对象bind()方法兼容处理

    bind() 函数在 ECMA-262 第五版才被加入:它可能无法在所有浏览器上运行.你可以部份地在脚本开头加入以下代码,就能使它运作,让不支持的浏览器也能使用 bind() 功能 if (!Func ...

  2. 使用Python完成排序(冒泡、选择、插入法)

    class Sort(object): @staticmethod def bubble_sort(ls): lenth = len(ls) if lenth == 0: return [] whil ...

  3. liunx基础命令

    linux的简单介绍 linux是一款免费使用和自由传播的内似于unix的操作系统软件,是一个基于POSI和unix的多用户,多任务,支持多线程和多CPU的一种操作系统.主要用于服务器,特别是网络服务 ...

  4. OpenCV+VisualStudion2017配置

    1.创建工程 Ctrl+Shift+n 2.创建.cpp文件 Ctrl+Shift+a 3.打开属性管理器 视图->其他窗口->属性管理器 Debug64右键 属性 4.添加包含目录 VC ...

  5. IntelliJ IDEA 2017版 spring-boot 2.0.5 邮件发送简单实例 (三)

    一.搭建SpringBoot项目 详见此文:https://www.cnblogs.com/liuyangfirst/p/8298588.html 注意: 需要添加mail依赖的包,同时还添加了lom ...

  6. C# 编码标准(三)

    一.代码注释 1.文档型注释 该类注释采用.Net已定义好的Xml标签来标记,在声明接口.类.方法.属性.字段都应该使用该类注释,以便代码完成后直接生成代码文档,让别人更好的了解代码的实现和接口.[示 ...

  7. ZOJ 3156 Taxi (二分 + 二分匹配)

    题意:给定 n 个人坐标, m 辆车的坐标,还有人的速度,要求每个人要进一辆不同的车,问你所有都进车的最短时间是多少. 析:首先二分时间 mid,很明显就是最后那个人进车的时间,然后如果把第 i 个人 ...

  8. HP 集群软件 - 不能接收节点的设备查询信息:软件引起的连接失败

    问题 # cmcheckconf -v -C /etc/cmcluster/cmclconfig.ascii Begin cluster verification...  Checking clust ...

  9. 2018-03-10 VCard备份恢复联系人

    主要在VCardComposer类中备份联系人的逻辑 导出流程: http://blog.csdn.net/michael_yt/article/details/78270537 导入流程: http ...

  10. MapGIS10.3新功能

    智能的GIS 支持开放的数据集.数据库.等等 T-C-V 软件结构是继局部网软件的 C/S 结构,互联网软件的 B/S 结构发展起来的适合云 计算.云服务的新一代软件三层结构,分别为终端应用层(T 层 ...