Guava Cache的使用方式

maven依赖

 <!--Guava Cache-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.2-jre</version>
        </dependency>

1、Guava Cache创建方式

GuavaCache有两种创建方式:CacheLoaderCallable callback

package com.lg.guava.demo.guava;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.lg.guava.demo.Constants;

import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;


public class Demo1 {
    /**
     * 初始化缓存三个
     */
    public static void initCache(LoadingCache<String,Object> cache) throws Exception {
        for(int i=1;i<=3;i++){
            //连接数据源   如果缓存没有则读取数据源
            cache.get(String.valueOf(i));
        }

    }

    /**
     * 显示缓存里的数据
     * @param cache
     */
    public static void display(LoadingCache<String,Object> cache){
        //利用迭代器
        Iterator its=cache.asMap().entrySet().iterator();
        while(its.hasNext()){
            System.out.println(its.next().toString());
        }
    }

    /**
     * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
     * @param key
     * @param cache
     */
    public static void get(String key,LoadingCache<String,Object> cache) throws Exception {
        cache.get(key, new Callable<Object>() {

            @Override //回调方法用于读源并写入缓存
            public Object call() throws Exception {
                //读源
                Object value=Constants.hm.get(key);
                //写回缓存
               // cache.put(key,value);
                return  value;
            }
        });
    }

    public static void main(String[] args) throws Exception {
        //CacheLoader的方式创建
        LoadingCache<String,Object> cache= CacheBuilder.newBuilder().build(new CacheLoader<String, Object>() {

            //读取数据源
            @Override
            public Object load(String key) throws Exception {
                return Constants.hm.get(key);
            }
        });
        //初始化缓存
        initCache(cache);
        System.out.println(cache.size());
        //显示缓存数据
        display(cache);
        //读取4
        get("4",cache);
        System.out.println("==================================");
        display(cache);
        System.out.println("==================================");
        display(cache);
    }
}

2、缓存数据删除

GuavaCache的数据删除分为:被动删除主动删除

2.1、被动删除

(1)基于数据大小的删除

LoadingCache<String,Object> cache= CacheBuilder.newBuilder()
        /*
            加附加的功能
        */
        //最大个数
        .maximumSize(3)
        .build(new CacheLoader<String, Object>() {
    //读取数据源
    @Override
    public Object load(String key) throws Exception {
        return Constants.hm.get(key);
    }
});
//读取缓存中的1的数据 缓存有就读取 没有就返回null
        System.out.println(cache.getIfPresent("5"));
    //读取4 读源并回写缓存 淘汰一个(LRU+FIFO)
        get("4",cache);
  • 规则:LRU+FIFO
  • 访问次数一样少的情况下,FIFO
package com.lg.guava.demo.guava;

import com.google.common.cache.*;
import com.lg.guava.demo.Constants;

import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;


public class Demo2 {
    /**
     * 初始化缓存三个
     */
    public static void initCache(LoadingCache<String, Object> cache) throws Exception {
        for (int i = 1; i <= 3; i++) {
            //连接数据源   如果缓存没有则读取数据源
            cache.get(String.valueOf(i));
        }

    }

    /**
     * 显示缓存里的数据
     *
     * @param cache
     */
    public static void display(LoadingCache<String, Object> cache) {
        //利用迭代器
        Iterator its = cache.asMap().entrySet().iterator();
        while (its.hasNext()) {
            System.out.println(its.next().toString());
        }
    }

    /**
     * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
     *
     * @param key
     * @param cache
     */
    public static void get(String key, LoadingCache<String, Object> cache) throws Exception {
        cache.get(key, new Callable<Object>() {

            @Override //回调方法用于读源并写入缓存
            public Object call() throws Exception {
                //读源
                Object value = Constants.hm.get(key);
                //写回缓存
                // cache.put(key,value);
                return value;
            }
        });
    }

    public static void main(String[] args) throws Exception {
        //CacheLoader的方式创建
        LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
                /*
                     加附加的功能
                 */
                //最大个数
                .maximumSize(3)
                .concurrencyLevel(Runtime.getRuntime().availableProcessors())
                .refreshAfterWrite(1, TimeUnit.SECONDS)
                //统计命中率
                .recordStats()
                //移除通知
                .removalListener(new RemovalListener<Object, Object>() {

                    @Override
                    public void onRemoval(RemovalNotification<Object, Object> removalNotification) {
                        //移除的key 移除的原因
                        System.out.println(removalNotification.getKey() + ":" + removalNotification.getCause());
                    }
                })
                .build(new CacheLoader<String, Object>() {

                    //读取数据源
                    @Override
                    public Object load(String key) throws Exception {
                        return Constants.hm.get(key);
                    }
                });
        //初始化缓存
        initCache(cache);
        System.out.println(cache.size());
        //显示缓存数据
        display(cache);
        //读取缓存中的1的数据    缓存有就读取 没有就返回null
        System.out.println(cache.getIfPresent("1"));

        //读取4   读源并回写缓存  淘汰一个(LRU+FIFO)
        get("4", cache);
        System.out.println("==================================");
        display(cache);

        //打印输出统计
        System.out.println(cache.stats().toString()+", " + cache.stats().hitRate());

    }
}

(2)基于过期时间的删除

隔多长时间后没有被访问过的key被删除

.maximumSize(3).expireAfterAccess(3, TimeUnit.SECONDS)

写入多长时间后过期

//等同于expire ttl 缓存中对象的生命周期就是3秒
.maximumSize(3).expireAfterWrite(3, TimeUnit.SECONDS)

package com.lg.guava.demo.guava;

import com.google.common.cache.*;
import com.lg.guava.demo.Constants;

import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;


public class Demo2 {
    /**
     * 初始化缓存三个
     */
    public static void initCache(LoadingCache<String, Object> cache) throws Exception {
        for (int i = 1; i <= 3; i++) {
            //连接数据源   如果缓存没有则读取数据源
            cache.get(String.valueOf(i));
        }

    }

    /**
     * 显示缓存里的数据
     *
     * @param cache
     */
    public static void display(LoadingCache<String, Object> cache) {
        //利用迭代器
        Iterator its = cache.asMap().entrySet().iterator();
        while (its.hasNext()) {
            System.out.println(its.next().toString());
        }
    }

    /**
     * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
     *
     * @param key
     * @param cache
     */
    public static void get(String key, LoadingCache<String, Object> cache) throws Exception {
        cache.get(key, new Callable<Object>() {

            @Override //回调方法用于读源并写入缓存
            public Object call() throws Exception {
                //读源
                Object value = Constants.hm.get(key);
                //写回缓存
                // cache.put(key,value);
                return value;
            }
        });
    }

    public static void main(String[] args) throws Exception {
        //CacheLoader的方式创建
        LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
                /*
                     加附加的功能
                 */
                //最大个数
                .maximumSize(3)
                .concurrencyLevel(Runtime.getRuntime().availableProcessors())
                .refreshAfterWrite(1, TimeUnit.SECONDS)
                //统计命中率
                .recordStats()
                //移除通知
                .removalListener(new RemovalListener<Object, Object>() {

                    @Override
                    public void onRemoval(RemovalNotification<Object, Object> removalNotification) {
                        //移除的key 移除的原因
                        System.out.println(removalNotification.getKey() + ":" + removalNotification.getCause());
                    }
                })
                .build(new CacheLoader<String, Object>() {

                    //读取数据源
                    @Override
                    public Object load(String key) throws Exception {
                        return Constants.hm.get(key);
                    }
                });
        //初始化缓存
        initCache(cache);
        System.out.println(cache.size());
        //显示缓存数据
        display(cache);
        //读取缓存中的1的数据    缓存有就读取 没有就返回null
        System.out.println(cache.getIfPresent("1"));

        //读取4   读源并回写缓存  淘汰一个(LRU+FIFO)
        get("4", cache);
        System.out.println("==================================");
        display(cache);

        //打印输出统计
        System.out.println(cache.stats().toString()+", " + cache.stats().hitRate());

    }
}
package com.lg.guava.demo.guava;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.lg.guava.demo.Constants;

import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;


public class Demo3 {
    /**
     * 初始化缓存三个
     */
    public static void initCache(LoadingCache<String,Object> cache) throws Exception {
        for(int i=1;i<=3;i++){
            //连接数据源   如果缓存没有则读取数据源
            cache.get(String.valueOf(i));
        }

    }

    /**
     * 显示缓存里的数据
     * @param cache
     */
    public static void display(LoadingCache<String,Object> cache){
        //利用迭代器
        Iterator its=cache.asMap().entrySet().iterator();
        while(its.hasNext()){
            System.out.println(its.next().toString());
        }
    }

    /**
     * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
     * @param key
     * @param cache
     */
    public static void get(String key,LoadingCache<String,Object> cache) throws Exception {
        cache.get(key, new Callable<Object>() {

            @Override //回调方法用于读源并写入缓存
            public Object call() throws Exception {
                //读源
                Object value=Constants.hm.get(key);
                //写回缓存
               // cache.put(key,value);
                return  value;
            }
        });
    }

    public static void main(String[] args) throws Exception {
        //CacheLoader的方式创建
        LoadingCache<String,Object> cache= CacheBuilder.newBuilder()
                /*
                     加附加的功能
                 */
                                //等同于expire  ttl  缓存中对象的生命周期就是3秒
                .maximumSize(3).expireAfterWrite(3, TimeUnit.SECONDS)
                .build(new CacheLoader<String, Object>() {

            //读取数据源
            @Override
            public Object load(String key) throws Exception {
                return Constants.hm.get(key);
            }
        });
        //初始化缓存
        initCache(cache);
        System.out.println(cache.size());
        //显示缓存数据
            display(cache);
            Thread.sleep(1000);
            //访问1
            cache.getIfPresent("1");

            //歇了2.1秒
            Thread.sleep(2100);

            System.out.println("==================================");
            display(cache);

    }
}

(3)基于引用的删除

        可以通过weakKeys和weakValues方法指定Cache只保存对缓存记录key和value的弱引用。这样当没有其他强引用指向key和value时,key和value对象就会被垃圾回收器回收。

LoadingCache<String,Object> cache = CacheBuilder.newBuilder()
        // 最大3个 值的弱引用
        .maximumSize(3).
weakValues()
        .build(
);

package com.lg.guava.demo.guava;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.lg.guava.demo.Constants;

import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;


public class Demo4 {
    /**
     * 初始化缓存三个
     */
    public static void initCache(LoadingCache<String, Object> cache) throws Exception {
        for (int i = 1; i <= 3; i++) {
            //连接数据源   如果缓存没有则读取数据源
            cache.get(String.valueOf(i));
        }

    }

    /**
     * 显示缓存里的数据
     *
     * @param cache
     */
    public static void display(LoadingCache<String, Object> cache) {
        //利用迭代器
        Iterator its = cache.asMap().entrySet().iterator();
        while (its.hasNext()) {
            System.out.println(its.next().toString());
        }
    }

    /**
     * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
     *
     * @param key
     * @param cache
     */
    public static void get(String key, LoadingCache<String, Object> cache) throws Exception {
        cache.get(key, new Callable<Object>() {

            @Override //回调方法用于读源并写入缓存
            public Object call() throws Exception {
                //读源
                Object value = Constants.hm.get(key);
                //写回缓存
                // cache.put(key,value);
                return value;
            }
        });
    }

    public static void main(String[] args) throws Exception {
        //CacheLoader的方式创建
        LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
                /*
                     加附加的功能
                 */
                //弱值的删除
                .maximumSize(3).weakValues()
                .build(new CacheLoader<String, Object>() {

                    //读取数据源
                    @Override
                    public Object load(String key) throws Exception {
                        return Constants.hm.get(key);
                    }
                });
        //初始化缓存
        initCache(cache);
        System.out.println(cache.size());
        //显示缓存数据
        display(cache);

        Object v = new Object();
        cache.put("1", v);

        v = new Object(); //原对象不再有强引用
        //强制垃圾回收
        System.gc();
        System.out.println("================================");
        display(cache);


    }
}

2.2、主动删除

(1)单独删除

// 将key=1 删除
cache.invalidate("1");

(2)批量删除

// 将key=1和2的删除
cache.invalidateAll(Arrays.asList("1","2"));

(3)清空所有数据

// 清空缓存
cache.invalidateAll();

package com.lg.guava.demo.guava;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.lg.guava.demo.Constants;

import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.Callable;

/**
 * 主动删除
 */
public class Demo5 {
    /**
     * 初始化缓存三个
     */
    public static void initCache(LoadingCache<String, Object> cache) throws Exception {
        for (int i = 1; i <= 3; i++) {
            //连接数据源   如果缓存没有则读取数据源
            cache.get(String.valueOf(i));
        }

    }

    /**
     * 显示缓存里的数据
     *
     * @param cache
     */
    public static void display(LoadingCache<String, Object> cache) {
        //利用迭代器
        Iterator its = cache.asMap().entrySet().iterator();
        while (its.hasNext()) {
            System.out.println(its.next().toString());
        }
    }

    /**
     * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
     *
     * @param key
     * @param cache
     */
    public static void get(String key, LoadingCache<String, Object> cache) throws Exception {
        cache.get(key, new Callable<Object>() {

            @Override //回调方法用于读源并写入缓存
            public Object call() throws Exception {
                //读源
                Object value = Constants.hm.get(key);
                //写回缓存
                // cache.put(key,value);
                return value;
            }
        });
    }

    public static void main(String[] args) throws Exception {
        //CacheLoader的方式创建
        LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
                /*
                     加附加的功能
                 */
                //弱值的删除
                .maximumSize(3).weakValues()
                .build(new CacheLoader<String, Object>() {

                    //读取数据源
                    @Override
                    public Object load(String key) throws Exception {
                        return Constants.hm.get(key);
                    }
                });
        //初始化缓存
        initCache(cache);
        System.out.println(cache.size());
        //显示缓存数据
        display(cache);
        System.out.println("================================");
        //清空
     //   cache.invalidateAll();
        //删除指定key
      //  cache.invalidate("1");
        //删多个
        cache.invalidateAll(Arrays.asList("1","3"));
        display(cache);


    }
}

3、Guava Cache原理

3.1、GuavaCache核心原理之数据结构

  • Guava Cache的数据结构跟ConcurrentHashMap类似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。
  • 相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。其数据结构图如下:

  • LocalCache为Guava Cache的核心类,包含一个Segment数组组成
  • Segement数组的长度决定了cache的并发数
  • 每一个Segment使用了单独的锁,其实每个Segment继承了ReentrantLock,对Segment的写操作需要先拿到锁
  • 每个Segment由一个table和5个队列组成 
  • 5个队列:
    • ReferenceQueue keyReferenceQueue : 已经被GC,需要内部清理的键引用队列
    • ReferenceQueue valueReferenceQueue : 已经被GC,需要内部清理的值引用队列
    • ConcurrentlinkedQueue<ReferenceEntry<k,v>> recencyQueue : LRU队列,当segment上达到临界值发生写操作时该队列会移除数据
    • Queue<ReferenceEntry<K, V>> writeQueue:写队列,按照写入时间进行排序的元素队列,写入一个元素时会把它加入到队列尾部
    • Queue<ReferenceEntry<K, V>> accessQueue:访问队列,按照访问时间进行排序的元素队列,访问(包括写入)一个元素时会把它加入到队列尾部
  • 1个table:

         AtomicReferenceArray<ReferenceEntry<K, V>> table:AtomicReferenceArray可以用原子方式更新其元素的对象引用数组

  • ReferenceEntry<k,v>
    • ReferenceEntry是Guava Cache中对一个键值对节点的抽象,每个ReferenceEntry数组项都是一条ReferenceEntry链。并且一个ReferenceEntry包含key、hash、valueReference、next字段(单链)
    • Guava Cache使用ReferenceEntry接口来封装一个键值对,而用ValueReference来封装Value值

3.2、GuavaCache核心原理之回收机制

Guava Cache提供了三种基本的缓存回收方式:

基于容量回收:在缓存项的数目达到限定值之前,采用LRU的回收方式

定时回收:

  • expireAfterAccess:缓存项在给定时间内没有被读/写访问,则回收。回收顺序和基于大小回收一样(LRU)
  • expireAfterWrite:缓存项在给定时间内没有被写访问(创建或覆盖),则回收

基于引用回收:通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以垃圾回收

除了以上三种还有主动删除,采用命令,这个前面讲了

  • GuavaCache构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。
  • GuavaCache是在每次进行缓存操作的时候,惰性删除 如get()或者put()的时候,判断缓存是否过期

3.3、GuavaCache核心原理之Segment定位

  • 先通过key做hash定位到所在的Segment
  • 通过位运算找首地址的偏移量 SegmentCount>=并发数且为2的n次方
V get(K key, CacheLoader<? super K, V> loader) throws ExecutionException {
        // 注意,key不可为空
        int hash = hash(checkNotNull(key));
        // 通过hash定位到segment数组的某个Segment元素,然后调用其get方法
        return segmentFor(hash).get(key, hash, loader);
}

再找到segment中的Entry链数组,通过key的hash定位到某个Entry节点

V get(K key, int hash, CacheLoader<? super K, V> loader) throws ExecutionException {
        checkNotNull(key);
        checkNotNull(loader);
        try {
                if (count != 0) { // read-volatile
                // 内部也是通过找Entry链数组定位到某个Entry节点
                        ReferenceEntry<K, V> e = getEntry(key, hash);
                        ......

4、Guava Cache高级实战

4.1、GuavaCache高级实战之并发操作

(1)并发设置

GuavaCache通过设置 concurrencyLevel 使得缓存支持并发的写入和读取

LoadingCache<String,Object> cache = CacheBuilder.newBuilder()
    // 最大3个 同时支持CPU核数线程写缓存
    .maximumSize(3).concurrencyLevel(Runtime.getRuntime().availableProcessors()).build();

concurrencyLevel=Segment数组的长度

同ConcurrentHashMap类似Guava cache的并发也是通过分离锁实现

V get(K key, CacheLoader<? super K, V> loader) throws ExecutionException {
    int hash = this.hash(Preconditions.checkNotNull(key));
    //通过hash值确定该key位于哪一个segment上,并获取该segment
    return this.segmentFor(hash).get(key, hash, loader);
}

        LoadingCache采用了类似ConcurrentHashMap的方式,将映射表分为多个segment。segment之间可以并发访问,这样可以大大提高并发的效率,使得并发冲突的可能性降低了。

(2)更新锁定

  • GuavaCache提供了一个refreshAfterWrite定时刷新数据的配置项
  • 如果经过一定时间没有更新或覆盖,则会在下一次获取该值的时候,会在后台异步去刷新缓存
  • 刷新时只有一个请求回源取数据,其他请求会阻塞(block)在一个固定时间段,如果在该时间段内没有获得新值则返回旧值。
LoadingCache<String,Object> cache = CacheBuilder.newBuilder()
    // 最大3个 同时支持CPU核数线程写缓存
    .maximumSize(3).concurrencyLevel(Runtime.getRuntime().availableProcessors())
    //3秒内阻塞会返回旧数据
    .refreshAfterWrite(3,TimeUnit.SECONDS).build();

4.2、GuavaCache高级实战之动态加载

        动态加载行为发生在获取不到数据或者是数据已经过期的时间点,Guava动态加载使用回调模式用户自定义加载方式,然后Guava cache在需要加载新数据时会回调用户的自定义加载方式

segmentFor(hash).get(key, hash, loader)

loader即为用户自定义的数据加载方式,当某一线程get不到数据会去回调该自定义加载方式去加载数据

4.3、GuavaCache高级实战之自定义LRU算法

package com.lg.guava.demo.guava.lru;

import java.util.LinkedHashMap;
import java.util.Map;

public class LRUcache<k, v> extends LinkedHashMap<k, v> {
    private final int limit;

    public LRUcache(int limit) {
        //初始化 accessOrder : true 改变尾结点
        super(16, 0.75f, true);
        this.limit = limit;
    }

    //是否删除最老的数据
    @Override
    protected boolean removeEldestEntry(Map.Entry<k, v> eldest) {
        return size() > limit;
    }


}
package com.lg.guava.demo.guava.lru;

public class LinkedHashLRUcache<k, v> {
    /**
     * LinkedHashMap(自身实现了LRU算法)
     * 有序
     * 每次访问一个元素,都会加到尾部
     */


    int limit;
    LRUcache<k, v> lruCache;


    public LinkedHashLRUcache(int limit) {

        this.limit = limit;
        this.lruCache = new LRUcache(limit);
    }


    public void put(k key, v value) {

        this.lruCache.put(key, value);
    }

    public v get(k key) {

        return this.lruCache.get(key);
    }


    public static void main(String[] args) {
        LinkedHashLRUcache lru = new LinkedHashLRUcache(3);
        lru.put(1, "zhangfei1");
        lru.put(2, "zhangfei2");
        lru.put(3, "zhangfei3");
        lru.get(1);
        lru.put(4, "zhangfei4");
        for (Object o : lru.lruCache.values()) {
            System.out.println(o.toString());
        }
    }
}

4.4、GuavaCache高级实战之疑难问题

(1)GuavaCache会oom(内存溢出)吗

会,当我们设置缓存永不过期(或者很长),缓存的对象不限个数(或者很大)时,比如:

Cache<String, String> cache = CacheBuilder.newBuilder()
    .expireAfterWrite(100000, TimeUnit.SECONDS)
    .build();

不断向GuavaCache加入大字符串,最终将会oom

解决方案:缓存时间设置相对小些,使用弱引用方式存储对象

Cache<String, String> cache = CacheBuilder.newBuilder()
    .expireAfterWrite(1, TimeUnit.SECONDS)
    .weakValues().build();

(2)GuavaCache缓存到期就会立即清除吗

不是的,GuavaCache是在每次进行缓存操作的时候,如get()或者put()的时候,判断缓存是否过期

void evictEntries(ReferenceEntry<K, V> e) {
    drainRecencyQueue();
    while ((e = writeQueue.peek()) != null && map.isExpired(e, now)) {
        if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
            throw new AssertionError();
        }
    }
    while ((e = accessQueue.peek()) != null && map.isExpired(e, now)) {
        if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
            throw new AssertionError();
        }
    }
}

        一个如果一个对象放入缓存以后,不在有任何缓存操作(包括对缓存其他key的操作),那么该缓存不会主动过期的。

(3)GuavaCache如何找出最久未使用的数据

  • 用accessQueue,这个队列是按照LRU的顺序存放的缓存对象(ReferenceEntry)的。会把访问过的对象放到队列的最后。
  • 并且可以很方便的更新和删除链表中的节点,因为每次访问的时候都可能需要更新该链表,放入到链表的尾部。
  • 这样,每次从access中拿出的头节点就是最久未使用的。
  • 对应的writeQueue用来保存最久未更新的缓存队列,实现方式和accessQueue一样。

5、Guava Cache源码剖析

GuavaCache源码剖析之实现框架

GuavaCache体系类图:

  • CacheBuilder:类,缓存构建器。构建缓存的入口,指定缓存配置参数并初始化本地缓存。CacheBuilder在build方法中,会把前面设置的参数,全部传递给LocalCache,它自己实际不参与任何计算

    (1)缓存构建器。构建缓存的入口,指定缓存配置参数并初始化本地缓存。
    (2)主要采用builder的模式,CacheBuilder的每一个方法都返回这个CacheBuilder知道build方法的调用。注意build方法有重载,带有参数的为构建一个具有数据加载功能的缓存,不带参数的构建一个没有数据加载功能的缓存。

  • CacheLoader:抽象类。用于从数据源加载数据,定义load、reload、loadAll等操作
  • Cache:接口,定义get、put、invalidate等操作,这里只有缓存增删改的操作,没有数据加载的操作
  • LoadingCache:接口,继承自Cache。定义get、getUnchecked、getAll等操作,这些操作都会从数据源load数据
  • LocalCache:类。整个guava cache的核心类,包含了guava cache的数据结构以及基本的缓存的操作方法

    (1)LoadingCache这些类表示获取Cache的方式,可以有多种方式,但是它们的方法最终调用到LocalCache的方法,LocalCache是Guava Cache的核心类。

    (2)LocalCache为Guava Cache的核心类 LocalCache的数据结构与ConcurrentHashMap很相似,都由多个segment组成,且各segment相对独立,互不影响,所以能支持并行操作。

    (3)每个segment由一个table和若干队列组成。缓存数据存储在table中,其类型为AtomicReferenceArray。

  • LocalManualCache:LocalCache内部静态类,实现Cache接口。其内部的增删改缓存操作全部调用成员变量localCache(LocalCache类型)的相应方法
  • LocalLoadingCache:LocalCache内部静态类,继承自LocalManualCache类,实现LoadingCache接口。其所有操作也是调用成员变量localCache(LocalCache类型)的相应方法 
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悠然予夏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值