Java Caffeine本地缓存实现

Caffeine:是一个高性能的Java本地缓存,caffeine这样的本地缓存和concurrentMap很像,都支持并发,支持o(1)时间复杂度的数据存储。
区别:
concurrentMap将存储所有存入的数据,知道你显式将其删除;
caffeine将通过给定的配置,自动移除“不常用”的数据,以保持内存的合理占用。
一、caffeine的常见用法
1.Cache
在 获取缓存值时,如果向在缓存值不存在时,原子地将值写入缓存,则可以调用get(key,k->value)方法,该方法将避免写入竞争。
调用invalidate()方法,将手动移除缓存。
多线程情况下,当使用get(key,k->value)时,如果有另一个线程同时调用本方法进行竞争,则后一线程会被堵塞,直到前一线程更新缓存完成,
若另一线程调用getifPresent()方法,则立即返回null,不会被堵塞。

Cache<String,String> caches = Caffeine.newBuilder().build();
        caches.getIfPresent("123"); // 如果该Key没有value则返回null
        caches.get("123",k -> "456");// 如果该Key没有value则返回指定的值 `456`
        caches.getIfPresent("123"); // 456
        caches.put("123","789"); //重新赋值
        caches.getIfPresent("123"); // 789

2.LoadingCache
是一种自动加载的缓存。其和普通缓存不同的地方在于,当缓存不存在/过期时,
若调用get()方法,则会自动调用CacheLoader.load()方法加载最新值。调用getAll()方法遍历所有的Key调用get()
除非实现了CacheLoader.LoadAll()方法。
使用LoadingCache时,需要指定CacheLoader,并实现其中的load()方法提供话u村确实时自动加载。
多线程情况下,当两个线程同时调用get(),则后一线程将被阻塞,直至前一线程更新缓存完成。

LoadingCache<String,String> cache = Caffeine.newBuilder().build(new CacheLoader<String, String>() {
            @Override
            public  String load(@NonNull String s) throws Exception {
                return "value1"; // 如果通过Key在缓存中查不到,则返回该值
            }

            @Override
            public @NonNull Map<String, String> loadAll(@NonNull Iterable<? extends  String> keys) throws Exception {
                return null;
            }
        });
        System.out.println(cache.get("123")); // value1

3.AsyncCache 
是Cache的一个变体,其响应结果均为CompletableFuture,通过着种方式,AsyncCache对异步编程模式进行了适配。
默认情况下,缓存计算使用ForkJoinPool.commonPool()作为线程池,如果想要指定线程池,则可以覆盖并实现Caffeine.executor(Executor)方法。

多线程情况下,当线程同时调用get(key,k->value),则会返回同一个CompletableFuture对象。
由于返回结果本身不进行阻塞,可以根据业务涉及自行选择阻塞等待或非阻塞。

AsyncCache<String,String> asyncCache = Caffeine.newBuilder().buildAsync();
        CompletableFuture<String> key = asyncCache.get("key", k -> "abc");
        try {
            key.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }

4.AsyncLoadingCache 
是LoadingCache 和AsyncCache 功能的组合。AsyncLoadingCache 支持异步的方式,对缓存进行自动加载。
类似LoadingCach,同样需要指定CacheLoader,并实现其中的load()方法供缓存缺失时自动加载,该方法将自动在
ForkJoinPool.commonPool()线程池中提交。如果想要指定Executor,则可以实现AsyncCacheLoader().asyncLoad()方法。

AsyncLoadingCache<String,String> asyncLoadingCache = Caffeine.newBuilder()
                .buildAsync(new CacheLoader<String, String>() {
                    @Override
                    public @Nullable String load(@NonNull String s) throws Exception {
                        return "value_old";
                    }

                    @Override
                    //自定义线程池加载
                    public @NonNull CompletableFuture<String> asyncLoad(@NonNull String key, @NonNull Executor executor) {
                        return CacheLoader.super.asyncLoad(key, executor);
                    }
                });
        CompletableFuture<String> key = asyncLoadingCache.get("key");
        try {
            key.get(); // 阻塞,直至缓存更新完成
        } catch (InterruptedException |ExecutionException e) {
            throw new RuntimeException(e);
        }

二、caffeine的驱逐策略/刷新机制
使用刷新机制refreshAfterWrite(),Caffeine将在key允许刷新后的首次访问时,立即返回旧数据,同时异步对缓存值进行刷新,
这使得调用方法不至于因为缓存驱逐二被阻塞。
需要注意的是,刷新机制只支持LoadingCache 和 AsyncLoadingCache。

initialCapacity:初始的缓存空间大小
maximumSize:缓存的最大数量
maximumWeight:缓存的最大权重
expireAfterAccess:最后一次读或写操作后经过指定时间过期
expireAfterWrite:最后一次写操作后经过指定时间过期
refreshAfterWrite:创建缓存或者最近一次更新缓存后经过指定时间间隔,刷新缓存
weakKeys:打开key的弱引用
weakValues:打开value的弱引用
softValues:打开value的软引用
recordStats:开发统计功能
注意:

expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准。
maximumSize和maximumWeight不可以同时使用。

Cache<String,String> caches = Caffeine.newBuilder()
                //caches的最初容量
                .initialCapacity(1)
                //caches的最大缓存数量
                .maximumSize(100)
                //设置写缓存后n秒钟过期
                .expireAfterWrite(10000, TimeUnit.SECONDS)
                //设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
                //.expireAfterAccess(10000, TimeUnit.MILLISECONDS)
                .build();

三、删除

单个删除:Cache.invalidate(key)
批量删除:Cache.invalidateAll(keys)
删除所有缓存项:Cache.invalidateAll

Cache<String,String> caches = Caffeine.newBuilder().build();
        caches.put("cook","123456");
        caches.put("cooks","sssss");
        System.out.println( caches.getIfPresent("cook"));
        //String[] keys = {"cook","cooks"};
        List<String> keyList = new ArrayList<>();
        keyList.add("cook");
        keyList.add("cooks");
        caches.invalidate("cooks"); // 指定key删除
        caches.invalidateAll(keyList); // 批量key删除
        caches.invalidateAll(); //删除所有
        System.out.println( caches.getIfPresent("cook"));
        System.out.println( caches.getIfPresent("cooks"));

Caffeine 是一个高性能的 Java 本地缓存库,在实现多级缓存时,通常会结合本地缓存和远程缓存(如 Redis)来构建。以下是关于 Caffeine 本地缓存实现多级缓存的方法和相关知识: ### 实现思路 多级缓存的核心思路是利用 Caffeine 本地缓存的高性能处理大部分的缓存读取请求,当本地缓存未命中时,再从远程缓存(如 Redis)中获取数据。如果远程缓存也未命中,则从数据源(如数据库)中获取数据,并将数据依次更新到远程缓存本地缓存中。 ### 代码示例 以下是一个使用 Caffeine 和 Redis 实现多级缓存的示例代码: ```java import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import redis.clients.jedis.Jedis; import java.util.concurrent.TimeUnit; public class MultiLevelCache { // 初始化 Caffeine 本地缓存 private static final Cache<String, String> localCache = Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(); // 初始化 Redis 客户端 private static final Jedis redisClient = new Jedis("localhost", 6379); public static String get(String key) { // 先从本地缓存中获取数据 String value = localCache.getIfPresent(key); if (value != null) { return value; } // 本地缓存未命中,从 Redis 中获取数据 value = redisClient.get(key); if (value != null) { // 将数据更新到本地缓存 localCache.put(key, value); return value; } // Redis 也未命中,从数据源中获取数据 value = getDataFromDataSource(key); if (value != null) { // 将数据更新到 Redis 和本地缓存 redisClient.set(key, value); localCache.put(key, value); } return value; } private static String getDataFromDataSource(String key) { // 模拟从数据源中获取数据 return "Data for " + key; } public static void main(String[] args) { String key = "testKey"; String value = get(key); System.out.println("Value: " + value); } } ``` ### 代码解释 1. **Caffeine 本地缓存初始化**:使用 `Caffeine.newBuilder()` 方法创建一个 Caffeine 缓存实例,并设置缓存的最大容量和过期时间。 2. **Redis 客户端初始化**:使用 Jedis 客户端连接到 Redis 服务器。 3. **缓存读取逻辑**:在 `get` 方法中,首先尝试从本地缓存中获取数据,如果未命中,则从 Redis 中获取数据。如果 Redis 也未命中,则从数据源中获取数据,并将数据依次更新到 Redis 和本地缓存中。 ### 相关知识 - **Caffeine 缓存策略**:Caffeine 提供了多种缓存策略,如基于大小的驱逐策略(`maximumSize`)、基于时间的过期策略(`expireAfterWrite`、`expireAfterAccess`)等,可以根据实际需求进行配置。 - **Redis 缓存**:Redis 是一个高性能的键值对存储数据库,支持多种数据结构和丰富的缓存操作。在多级缓存中,Redis 通常作为二级缓存,用于存储大量的数据。 - **缓存一致性**:在多级缓存中,需要注意缓存一致性问题。当数据发生更新时,需要同时更新本地缓存和远程缓存,以保证数据的一致性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值