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

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

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. spring cloud Feign 使用 @RequestLine 注解遇到的问题

    package com.itmuch.cloud; import org.springframework.cloud.netflix.feign.FeignClient; import com.itm ...

  2. C# 遇到 which has a higher version than referenced assembly

    当C#遇到这种提示: which has a higher version than referenced assembly, 说明有两个或多个工程引用的dll的版本有出现不一样, 如: A工程引用l ...

  3. dfs序理解-hdu3887

    dfs序就是相当于把树转化成了一个区间,在区间上进行操作. void dfs(int u, int fa) { l[u]=++key; ; i=e[i].next) { int v=e[i].v; i ...

  4. Django之auth模块用户认证模块

    一.Auth模块 1)auth模块是什么.登录后台使用的账号密码,则就是使用的auth模块创建的表 Auth模块是Django自带的用户认证模块: 我们在开发一个网站的时候,无可避免的需要设计实现网站 ...

  5. Keras下的文本情感分析简介。与MLP,RNN,LSTM模型下的文本情感测试

    # coding: utf-8 # In[1]: import urllib.request import os import tarfile # In[2]: url="http://ai ...

  6. Codeforces 1093 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 GGG题手速慢了没有在比赛的时候码出来233,FFF题居然没想出来? 五道题滚粗. 先谈谈其他几道题. A题 传送门 不小心看错题 直接看奇 ...

  7. 2018.12.15 poj3415 Common Substrings(后缀自动机)

    传送门 后缀自动机基础题. 给两个字符串,让你求长度不小于kkk的公共子串的数量. 这题可以用后缀自动机解决废话 考虑对其中一个字串建出后缀自动机,然后用另一个在上面跑,注意到如果一个状态有贡献的话, ...

  8. mac os下 android studio真机调试

    http://www.cnblogs.com/developer-wang/p/6719555.html 如果没有 .bash_profile 只需要创建 .bash_profile,然后增加andr ...

  9. 利用xshell远程连接centos安装oracle11g时在图形界面登录

    1.首先给centos安装桌面环境.( yum groupinstall ‘GNOME Desktop’) 2.安装Xmanager软件 3.打开xshell,新建连接 填好主机和名称后,点击左侧连接 ...

  10. vue的cli中引入css文件

    在public文件中创建一个文件夹css,放进reset.css 在main.js中引入即可 import '../public/css/reset.css'就可以啦