本地缓存之Guava简单使用

使用场景

随着互联网用户越来越多,并发量、吞吐量越来越大

本地缓存的应用场景:

  1. 对性能有非常高的要求
  2. 不经常变化
  3. 占用内存不大
  4. 有访问整个集合的需求
  5. 数据允许不时时一致
    例如:拉勾网首页,由于首页经常被访问,可以将职位信息:java开发、大数据开发等放在本地缓存中。
    在这里插入图片描述

guava cache:高并发,不需要持久化
currentHashMap:高并发
Ehcached:持久化 二级缓存

Guava Cache 的优势

缓存机制淘汰算法可参考博文:本地缓存之LRU FIFO实现

  1. 缓存过期和淘汰机制:LRU
  2. 并发处理能力:类似CurrentHashMap,是线程安全的,采用了分段锁机制,将一个集合分成若干个人partiton ,每个Patrtiton一把锁,master多分区,利用segement作分区
  3. 更新锁定:GuavaCache可以在CacheLoader的load方法中加以控制,对同一个key,只让一个请求去读源并回填缓存,其他请求阻塞等待。.
  4. 集成数据源:而GuavaCache的get可以集成数据源,在从缓存中读取不到时可以从数据源中读取数据并回填缓
  5. 监控缓存加载/命中情况

Guava Cache使用

Cache创建:

方法作用
maximumSize容量
expireAfterWrite缓存项在给定时间内没有被写访问(创建或覆盖),则回收
recordStats缓存项在给定时间内没有被读/写访问,则回收
removalListener移除监听器
weakKeys弱引用存储键,当键没有其它(强或软)引用时,缓存项可以被垃圾回收
weakValues使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收
concurrencyLevel并发操作

CacheLoader、Callable功能相同,都是在调用get方法时候,如果缓存不存在则指定数据源加载

CacheLoader

在创建缓存对象初始化时使用

模拟数据源

    public static HashMap<Integer, Integer> sourceMap = new HashMap<>();

    static {
        for (int i = 0; i < 10; i++){
            sourceMap.put(i, i);
        }

    }

使用demo

    public static void main(String[] args) throws ExecutionException {
        LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().maximumSize(5).build(new CacheLoader<Object, Object>() {

            @Override
            public Object load(Object o) throws Exception {
                return sourceMap.get(o);
            }
        });

        for (int i = 0; i < 10; i++){
            cache.get(i);
        }
        System.out.println(cache.size());
        System.out.println(cache.asMap());
    }

在这里插入图片描述

Callable

调用get方法时当缓存数据不存在时候从数据源加载数据

使用demo:

    @Test
    public void call() throws ExecutionException {
        Cache<Object, Object> cache = CacheBuilder.newBuilder().maximumSize(5).build();
        Object value = cache.get(1, new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return sourceMap.get(1);
            }
        });
        System.out.println(value);
    }

在这里插入图片描述

删除

主动删除

    @Test
    public void doDel() throws ExecutionException {
        LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().maximumSize(5).expireAfterAccess(3, TimeUnit.SECONDS).build(new CacheLoader<Object, Object>() {
            @Override
            public Object load(Object o) throws Exception {
                return sourceMap.get(o);
            }
        });
        initCache(cache);
        //主动删除 key为1
        cache.invalidate(1);
        System.out.println("主动删除");
        displayCache(cache);
        // 批量删除
        cache.invalidateAll(Arrays.asList(1,2));
        System.out.println("批量删除");
        displayCache(cache);
    }

过期删除

expireAfterAccess:如果在一定时间内没被访问则数据过期

    @Test
    public void expireTimeDel() throws ExecutionException, InterruptedException {
        LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().maximumSize(5).expireAfterAccess(3, TimeUnit.SECONDS).build(new CacheLoader<Object, Object>() {
            @Override
            public Object load(Object o) throws Exception {
                return sourceMap.get(o);
            }
        });

        initCache(cache);
        Thread.sleep(1000);
        cache.getIfPresent(1);
        Thread.sleep(2000);
        displayCache(cache);
    }

基于容量删除

    @Test
    public void sizeDel() throws ExecutionException {
        LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().maximumSize(1).build(new CacheLoader<Object, Object>() {

            @Override
            public Object load(Object o) throws Exception {
                return sourceMap.get(o);
            }
        });
        Object v = cache.get(1);
        System.out.println(v);
        //自动删除1
        Object v2 = cache.get(2);
        displayCache(cache);
    }

引用删除

开启weakValues功能,采用弱引用,对引用不了解的可以看看,我的文章

    @Test
    public void referenceDel(){
        Cache<Object, Object> cache = CacheBuilder.newBuilder().maximumSize(3).weakValues().build();
        cache.put("1",new Object());
        //强制垃圾回收
        System.gc();
        System.out.println(cache.getIfPresent("1"));
    }

高级用法

并发设置

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

Cache<Object, Object> cache = CacheBuilder.newBuilder().maximumSize(3).concurrencyLevel(Runtime.getRuntime().availableProcessors()).build();

更新锁定

GuavaCache提供了一个refreshAfterWrite定时刷新数据的配置项,如果经过一定时间没有更新或覆盖,则会在下一次获取该值的时候,会在后台异步去刷新缓存

刷新时只有一个请求回源取数据,其他请求会阻塞(block)在一个固定时间段,如果在该时间段内没有获得新值则返回旧值。

    @Test
    public void refresh() throws InterruptedException, ExecutionException {
        LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
                .maximumSize(3)
                .concurrencyLevel(Runtime.getRuntime().availableProcessors())
                .refreshAfterWrite(3, TimeUnit.SECONDS)
                .build(new CacheLoader<Integer, Integer>() {
                    @Override
                    public Integer load(Integer key) throws Exception {
                        return sourceMap.get(key);
                    }
                });

        cache.get(1);
        System.out.println("第一次取值: " + cache.getIfPresent(1));
        sourceMap.put(1, 10);
        Thread.sleep(5000);
        System.out.println("第二次取值: " + cache.getIfPresent(1));
    }

应用场景:accesstoken token失效 从公网拿token 采用更新锁定在这里插入图片描述

GuavaCache高级实战之疑难问题

GuavaCache会oom(内存溢出)吗

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

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

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

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

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

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

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

用accessQueue,这个队列是按照LRU的顺序放的缓存对象(ReferenceEntry)的,会把访问过的对象放在队列的最后。

并且可以很方便的更新和删除链表中的节点,因为每次访问的时候都可能需要更新链表,放入到链表的尾部。

这样,每次从access中拿出的头结点就是最久未使用的。

对应的writeQueue用来保存最久未更新的缓存队列,实现方式和accessQueue一样。

其他比较好的文章推荐:

中文教程

Guava总结好的博客

### 回答1: Guava本地缓存是Google Guava提供的一个缓存框架,它可以帮助我们在应用程序中高效地缓存数据。使用Guava本地缓存需要以下步骤: 1. 导入Guava依赖 在项目中导入Guava依赖,可以通过Maven或Gradle等方式进行导入。 2. 创建缓存对象 使用CacheBuilder来创建一个缓存对象,可以设置缓存的大小、过期时间等参数,例如: ``` Cache<String, Object> cache = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterAccess(10, TimeUnit.MINUTES) .build(); ``` 3. 将数据放入缓存 使用put方法将数据放入缓存中,例如: ``` cache.put("key1", "value1"); cache.put("key2", "value2"); ``` 4. 从缓存中获取数据 使用get方法从缓存中获取数据,例如: ``` Object value1 = cache.getIfPresent("key1"); Object value2 = cache.getIfPresent("key2"); ``` 5. 删除缓存中的数据 使用invalidate方法从缓存中删除数据,例如: ``` cache.invalidate("key1"); ``` 以上就是使用Guava本地缓存的基本步骤。需要注意的是,Guava本地缓存是线程安全的,可以在多线程环境中使用。同时,Guava本地缓存也提供了一些高级特性,例如缓存的回收策略、缓存的加载方式等,可以根据实际需求进行设置。 ### 回答2: Guava本地缓存是一种Google提供的使用简单且高效的缓存实现方式。下面是使用Guava本地缓存的步骤: 1. 首先,我们需要添加Guava库的依赖,确保项目中有Guava的jar包。 2. 创建一个缓存对象,可以使用CacheBuilder类的静态方法来创建一个新的缓存实例。例如: ```java Cache<String, String> cache = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterWrite(10, TimeUnit.MINUTES) .build(); ``` 上述代码创建了一个最大容量为100的缓存实例,并且设置了写入10分钟后过期。 3. 往缓存中存储数据,可以使用put方法将键值对存储到缓存中。例如: ```java cache.put("key1", "value1"); cache.put("key2", "value2"); ``` 上述代码将键值对(key1, value1)和(key2, value2)存储到缓存中。 4. 从缓存中获取数据,可以使用get方法根据键来获取对应的值。例如: ```java String value1 = cache.get("key1", () -> "default value"); ``` 上述代码尝试从缓存中获取键为key1的值,如果缓存中存在该键,则返回对应的值;如果缓存中不存在该键,则通过提供的回调函数生成一个默认值,并将该值存储到缓存中,然后返回该默认值。 5. 判断缓存中是否存在某个键,可以使用方法约束containsKey。例如: ```java boolean contains = cache.containsKey("key1"); ``` 上述代码判断缓存中是否存在键为key1的键值对。 6. 清空缓存中的所有数据,可以使用方法invalidateAll。例如: ```java cache.invalidateAll(); ``` 上述代码清空缓存中的所有键值对。 7. 在使用完缓存后,为了释放资源,可以使用方法invalidate来显式地使缓存无效,并且不会对缓存中的数据造成影响。例如: ```java cache.invalidate("key1"); ``` 上述代码使键为key1的键值对无效,但不会删除缓存中的其他数据。 通过以上步骤,我们可以灵活使用Guava本地缓存来提升程序的性能和效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值