Java内存缓存工具实现 - Guava LoadingCache

一、Guava介绍

  1. Guava是Google guava中的一个内存缓存模块,用于将数据缓存到JVM内存中。实际项目开发中经常将一些公共或者常用的数据缓存起来方便快速访问。
  2. Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果不符合需求,可以选择Memcached、Redis等工具。

二、代码示例

1. POM引入

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.1-jre</version>
</dependency>

2. 封装工具类

package com.soyoung.ad.engine.util;

import com.google.common.cache.*;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 功能描述
 *
 * @author 马振全 2020/1/13 16:18
 */
@Slf4j
public class CacheManager {

    /** 缓存项最大数量 */
    private static final long GUAVA_CACHE_SIZE = 100000;

    /** 缓存时间:天 */
    private static final long GUAVA_CACHE_DAY = 10;

    /** 缓存操作对象 */
    private static LoadingCache<Long, String> GLOBAL_CACHE = null;

    static {
        try {
            GLOBAL_CACHE = loadCache(new CacheLoader<Long, String>() {
                @Override
                public String load(Long key) throws Exception {
                    // 处理缓存键不存在缓存值时的处理逻辑
                    return "";
                }
            });
        } catch (Exception e) {
            log.error("初始化Guava Cache出错", e);
        }
    }

    /**
     * 全局缓存设置
     *
     * 缓存项最大数量:100000
     * 缓存有效时间(天):10
     *
     *
     * @param cacheLoader
     * @return
     * @throws Exception
     */
    private static LoadingCache<Long, String> loadCache(CacheLoader<Long, String> cacheLoader) throws Exception {
        LoadingCache<Long, String> cache = CacheBuilder.newBuilder()
                //缓存池大小,在缓存项接近该大小时, Guava开始回收旧的缓存项
                .maximumSize(GUAVA_CACHE_SIZE)
                //设置时间对象没有被读/写访问则对象从内存中删除(在另外的线程里面不定期维护)
                .expireAfterAccess(GUAVA_CACHE_DAY, TimeUnit.DAYS)
                // 设置缓存在写入之后 设定时间 后失效
                .expireAfterWrite(GUAVA_CACHE_DAY, TimeUnit.DAYS)
                //移除监听器,缓存项被移除时会触发
                .removalListener(new RemovalListener<Long, String>() {
                    @Override
                    public void onRemoval(RemovalNotification<Long, String> rn) {
                        //逻辑操作
                    }
                })
                //开启Guava Cache的统计功能
                .recordStats()
                .build(cacheLoader);
        return cache;
    }

    /**
     * 设置缓存值
     * 注: 若已有该key值,则会先移除(会触发removalListener移除监听器),再添加
     *
     * @param key
     * @param value
     */
    public static void put(Long key, String value) {
        try {
            GLOBAL_CACHE.put(key, value);
        } catch (Exception e) {
            log.error("设置缓存值出错", e);
        }
    }

    /**
     * 批量设置缓存值
     *
     * @param map
     */
    public static void putAll(Map<? extends Long, ? extends String> map) {
        try {
            GLOBAL_CACHE.putAll(map);
        } catch (Exception e) {
            log.error("批量设置缓存值出错", e);
        }
    }

    /**
     * 获取缓存值
     * 注:如果键不存在值,将调用CacheLoader的load方法加载新值到该键中
     *
     * @param key
     * @return
     */
    public static String get(Long key) {
        String token = "";
        try {
            token = GLOBAL_CACHE.get(key);
        } catch (Exception e) {
            log.error("获取缓存值出错", e);
        }
        return token;
    }

    /**
     * 移除缓存
     *
     * @param key
     */
    public static void remove(Long key) {
        try {
            GLOBAL_CACHE.invalidate(key);
        } catch (Exception e) {
            log.error("移除缓存出错", e);
        }
    }

    /**
     * 批量移除缓存
     *
     * @param keys
     */
    public static void removeAll(Iterable<Long> keys) {
        try {
            GLOBAL_CACHE.invalidateAll(keys);
        } catch (Exception e) {
            log.error("批量移除缓存出错", e);
        }
    }

    /**
     * 清空所有缓存
     */
    public static void removeAll() {
        try {
            GLOBAL_CACHE.invalidateAll();
        } catch (Exception e) {
            log.error("清空所有缓存出错", e);
        }
    }

    /**
     * 获取缓存项数量
     *
     * @return
     */
    public static long size() {
        long size = 0;
        try {
            size = GLOBAL_CACHE.size();
        } catch (Exception e) {
            log.error("获取缓存项数量出错", e);
        }
        return size;
    }
}

三、使用总结

1. 移除机制

guava做cache时候数据的移除分为被动移除主动移除两种。

被动移除分为三种:

  1. 基于大小的移除:数量达到指定大小,会把不常用的键值移除
  2. 基于时间的移除:expireAfterAccess(long, TimeUnit) 根据某个键值对最后一次访问之后多少时间后移除
            expireAfterWrite(long, TimeUnit) 根据某个键值对被创建或值被替换后多少时间移除
  3. 基于引用的移除:主要是基于java的垃圾回收机制,根据键或者值的引用关系决定移除

主动移除分为三种:1).单独移除:Cache.invalidate(key)

         2).批量移除:Cache.invalidateAll(keys)

         3).移除所有:Cache.invalidateAll()

如果配置了移除监听器RemovalListener,则在所有移除的动作时会同步执行该listener下的逻辑。

如需改成异步,使用:RemovalListeners.asynchronous(RemovalListener, Executor)

2. 遇到的问题

  1. 在put操作之前,如果已经有该键值,会先触发removalListener移除监听器,再添加
  2. 配置了expireAfterAccess和expireAfterWrite,但在指定时间后没有被移除。

     解决方案:CacheBuilder构建的缓存不会在特定时间自动执行清理和回收工作,也不会在某个缓存项过期后马上清理,它不会启动一个线程来进行缓存维护,因为a)线程相对较重,b)某些环境限制线程的创建。它会在写操作时顺带做少量的维护工作,或者偶尔在读操作时做。当然,也可以创建自己的维护线程,以固定的时间间隔调用Cache.cleanUp()。

原文地址:https://www.cnblogs.com/owenma/p/12191173.html

时间: 2024-10-09 17:49:52

Java内存缓存工具实现 - Guava LoadingCache的相关文章

Java内存缓存-通过Google Guava创建缓存

谷歌Guava缓存 Guava介绍 Guava是Google guava中的一个内存缓存模块,用于将数据缓存到JVM内存中.实际项目开发中经常将一些公共或者常用的数据缓存起来方便快速访问. Guava Cache是单个应用运行时的本地缓存.它不把数据存放到文件或外部服务器.如果不符合需求,可以选择Memcached.Redis等工具. 小案例 pom.xml添加guava依赖 <?xml version="1.0" encoding="UTF-8"?>

Java内存分页工具类

前言 工作过程中,经常会遇到基于内存数据进行分页处理的情况,例如批量更新数据库时,集合过大需要分批更新的情况,还有例如对缓存中的集合数据进行分页获取这种情况. 本文提供了通用的内存分页工具,参考了网络上的一些代码,主要基于 subList() 方法实现,希望对你有所帮助!工具类源码在本文底部. 本文原文链接地址:http://nullpointer.pw/Java%E5%86%85%E5%AD%98%E5%88%86%E9%A1%B5%E5%B7%A5%E5%85%B7%E7%B1%BB.htm

Java内存分析工具

内存分析工具 IDEA插件(VisualVM Launcher) 执行main函数的时候,同时启动jvisualvm,实时查看资源消耗情况.如图效果: Eclipse Memory Analyzer Open Source 原文地址:https://www.cnblogs.com/shengulong/p/11762236.html

Java内存缓存-通过Map定制简单缓存

缓存 在程序中,缓存是一个高速数据存储层,其中存储了数据子集,且通常是短暂性存储,这样日后再次请求此数据时,速度要比访问数据的主存储位置快.通过缓存,可以高效地重用之前检索或计算的数据. 为什么要用缓存 场景 在Java应用中,对于访问频率高,更新少的数据,通常的方案是将这类数据加入缓存中,相对从数据库中读取,读缓存效率会有很大提升. 在集群环境下,常用的分布式缓存有Redis.Memcached等.但在某些业务场景上,可能不需要去搭建一套复杂的分布式缓存系统,在单机环境下,通常是会希望使用内部

java内存泄漏工具查找

https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=4544bafe-c7a2-455f-9d43-eb866ea60091 ibm heapanalyzer

idea java内存分析工具

https://blog.csdn.net/qq_22194659/article/details/83829891 https://www.ej-technologies.com/products/jprofiler/overview.html L-J11-Everyone#speedzodiac-327a9wrs5dxvz#463a59 A-J11-Everyone#admin-3v7hg353d6idd5#9b4 原文地址:https://www.cnblogs.com/chenzecha

离线分析java内存

如题,我这里简单说下我现在离线分析java内存的方式,所谓离线,就是需要 dump出正在运行的java系统中的一些运行时堆栈数据,然后拿到线下来分析,分析可以包括内存,线程,GC等等,同时不会对正在运行的生产环境的机器 造成很大的影响,对应着离线分析,当然是在线分析了,这个我在后面会尝试下,因为离线分析有些场景还是模拟不出来,需要借助LR来模拟压力,查看在线的 java程序运行情况了. 首先一个简单的问题,如何dump出java运行时堆栈,这个SUN就提供了很好的工具,位于JAVA_HOME/b

Android内存分析工具DDMS heap + MAT 安装和使用

一  Java内存分析工具扫盲 如果像我一样一点都不了解,可以先进行内存分析工具扫盲 MAT介绍:     Eclipse Memory Analyzer(MAT)一个功能丰富的 JAVA 堆转储文件分析工具,可以用于发现内存漏洞和减少内存消耗. 二  Eclipse MAT插件安装 当前机器环境描述: [plain] view plaincopy 系统: Ubuntu 12.04 LTS 64 Eclipse for Mobile Developers Version: Juno Servic

使用google guava做内存缓存

google guava中有cache包,此包提供内存缓存功能.内存缓存需要考虑很多问题,包括并发问题,缓存失效机制,内存不够用时缓存释放,缓存的命中率,缓存的移除等等. 当然这些东西guava都考虑到了. guava中使用缓存需要先声明一个CacheBuilder对象,并设置缓存的相关参数,然后调用其build方法获得一个Cache接口的实例.请看下面的代码和注释,注意在注释中指定了Cache的各个参数. public static void main(String[] args) throw