博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Google guava cache源码解析1--构建缓存器(2)
阅读量:4978 次
发布时间:2019-06-12

本文共 6822 字,大约阅读时间需要 22 分钟。

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

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

CacheBuilder-->maximumSize(long size)

    /**     * 指定cache中最多能存放的entry(key-value)个数maximumSize     * 注意:     * 1、在entry个数还未达到这个指定个数maximumSize的时候,可能就会发生缓存回收     * 上边这种情况发生在cache size接近指定个数maximumSize,     * cache就会回收那些很少会再被用到的缓存(这些缓存会使最近没有被用到或很少用到的),其实说白了就是LRU算法回收缓存     * 2、maximumSize与maximumWeight不能一起使用,其实后者也很少会使用     */    public CacheBuilder
 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 
 LoadingCache
 build(CacheLoader
 loader) {        checkWeightWithWeigher();        return new LocalCache.LocalLoadingCache
(this, loader);    }

注意:

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

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

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

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

 

3.2、LocalCache

LocalLoadingCahe构造器

    static class LocalLoadingCache
 extends LocalManualCache
                                         implements LoadingCache
 {        LocalLoadingCache(CacheBuilder
 builder,                          CacheLoader
 loader) {            super(new LocalCache
(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
[] 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
 defaultLoader;    /** 默认的缓存加载器,用于做一些缓存加载操作(其实就是load),实现三步原子操作*/    @Nullable    final CacheLoader
 defaultLoader;

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

LocalCache-->LocalCache(CacheBuilder, CacheLoader)

    /**     * 创建一个新的、空的map(并且指定策略、初始化容量和并发水平)     */    LocalCache(CacheBuilder
 builder,               @Nullable CacheLoader
 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实例的创建过程,非常重要!!!

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

相关文章:

【推荐】 

转载于:https://www.cnblogs.com/zyfd/p/10138596.html

你可能感兴趣的文章
[转]http返回头中content-length与Transfer-Encoding: chunked的问题释疑
查看>>
display:table / display:table-cell 用法
查看>>
UITableView优化
查看>>
iOS 点击事件传递及响应
查看>>
谷歌应用商城打开
查看>>
Oracle RMAN备份
查看>>
实时监听输入框值变化的完美方案:oninput & onpropertychange
查看>>
【C】枚举用例分析
查看>>
Python中几种数据的常用内置方法
查看>>
安卓点击事件语法
查看>>
NOIP2012模拟赛第三弹
查看>>
7.22武汉日全食自拍留念
查看>>
[转帖]mimikatz 学习
查看>>
IIS 使用 HTTP重定向 修改 默认主页
查看>>
教程笔记《JavaScript深入浅出》
查看>>
MySQL——安装
查看>>
【转】移动端常用的四个框架
查看>>
[转载]delete指针之后应该赋值NULL
查看>>
thinkphp3.2导出
查看>>
OO第三阶段总结
查看>>