ExpiringMap使用详解

一、ExpiringMap概述

ExpiringMap项目地址:https://github.com/jhalterman/expiringmap

1、ExpiringMap简介

针对一些小体量的项目,存储的数据量也不是很大(如校验码)的情况下,使用 Redis会增加系统的复杂性和维护难度。

ExpiringMap就是一个轻量的Java缓存方式,它的功能和Redis类似。

ExpiringMap 是一个具有高性能,低开销,零依赖,由线程安全的 ConcurrentMap 实现过期 entries 等优点的 Map。

主要特点包括:过期策略、可变有效期、最大尺寸、侦听器过期、延迟输入加载、过期自省等。

ExpiringMap基本功能包括:

  • 可设置 Map 中的 Entry 在一段时间后自动过期。
  • 可设置 Map 最大容纳值,当到达 Maximum size 后,再次插入值会导致 Map 中的第一个值过期。
  • 可添加监听事件,在监听到 Entry 过期时调度监听函数。
  • 可以设置懒加载,在调用 get() 方法时创建对象。

2、ExpiringMap常用API

  • enum ExpirationPolicy:枚举,确定 ExpiringMap 元素应如何过期,
    • ACCESSED 根据上次访问的时间使它们过期,即从上次访问后开始计时;
    • CREATED 根据条目的创建时间使它们过期,即从创建之后开始计时。
  • enum TimeUnit:枚举,确定时间单位,MILLISECONDS 时间单位毫秒,SECONDS 时间单位秒,MINUTES 时间单位分钟,HOURS 时间单位小时,DAYS 时间单位天
  • interface EntryLoader<K, V>:按需加载,内有 V load(K key) 方法,调用时将 key 的新值加载到即将过期的映射中。
  • interface ExpiringEntryLoader<K, V>:按需加载,并控制每个值的过期时间(即可变过期时间),内有 ExpiringValue load(K key) 方法,调用时将 key 的新值加载到即将过期的映射中。
  • static final class Builder<K, V>:内部类,ExpiringMap 构造器。 默认为 ExpirationPolicy.CREATED,60 个 TimeUnit.SECONDS 到期和 Integer.MAX_VALUE 的 MAXSIZE。
  • static Builder<Object, Object> builder():创建一个 ExpiringMap 构造器,返回 ExpiringMap.Builder。
  • Builder<K, V> expiration(long duration, TimeUnit timeUnit):构造器中的方法,用于设置 Map 的过期时间,返回 ExpiringMap.Builder。- Builder<K, V> maxSize(int maxSize):设置 Map 的最大个数,超过限制仍要添加元素时将最先过期的元素过期。
  • int getMaxSize():获取 Map 的最大个数。
  • Builder<K1, V1> entryLoader(EntryLoader<? super K1, ? super V1> loader):构造器中的方法,可以设置 EntryLoader,EntryLoader 和 ExpiringEntryLoader,不能同时设置。
  • Builder<K1, V1> expiringEntryLoader(ExpiringEntryLoader<? super K1, ? super V1> loader):构造器中的方法,可以设置 ExpiringEntryLoader,EntryLoader 和 ExpiringEntryLoader,不能同时设置。
  • Builder<K, V> expirationPolicy(ExpirationPolicy expirationPolicy):构造器中的方法,用于设置 Map 的过期策略,返回 ExpiringMap.Builder。
  • Builder<K, V> variableExpiration():构造器中的方法,允许 Map 元素具有各自的到期时间,并允许更改到期时间。
  • Builder<K1, V1> expirationListener(ExpirationListener<? super K1, ? super V1> listener):构造器中的方法,配置监听每个 Map 元素过期, 通知传入的 ExpirationListener 执行预定好的操作。
  • void addExpirationListener(ExpirationListener<K, V> listener):给 ExpiringMap 添加过期监听器。
  • void removeExpirationListener(ExpirationListener<K, V> listener):移出 ExpiringMap 的过期监听。
  • long getExpiration(K key):获取指定 key 对应的元素的过期时间,返回毫秒值。
  • long getExpectedExpiration(K key):获取指定 key 预计到期时间,返货毫秒值。
  • ExpirationPolicy getExpirationPolicy(K key):获取指定 key 对应的元素的过期策略。
  • interface ExpirationListener<K, V>:过期监听接口,有一个 void expired(K key, V value) 方法,过期时自动调用。
  • ExpiringMap<K1, V1> build():创建并返回一个 ExpiringMap。

二、使用示例

使用 ExpiringMap之前,需要引入 pom依赖。

<!-- expiringmap依赖 -->
<dependency>
  <groupId>net.jodah</groupId>
  <artifactId>expiringmap</artifactId>
  <version>0.5.11</version>
</dependency>

1、构建时设置过期时间与过期策略

    /**
     * 初始化一个ExpiringMap(配置过期时间、过期协议等)
     */
    private final static ExpiringMap<String, String> expireCacheMap = ExpiringMap.builder()
            // 设置最大值,添加第11个entry时,会导致第1个立马过期(即使没到过期时间)。默认 Integer.MAX_VALUE
            .maxSize(10)
            // 允许 Map 元素具有各自的到期时间,并允许更改到期时间。
            .variableExpiration()
            // 设置过期时间,如果key不设置过期时间,key永久有效。
            .expiration(10, TimeUnit.SECONDS)
            .asyncExpirationListener((key, value) -> {
                log.info("expireCacheMap key数据被删除了 -> key={}, value={}", key, value);
            })
            //设置 Map 的过期策略
            .expirationPolicy(ExpirationPolicy.ACCESSED)
            .build();

    public static void main(String[] args) throws InterruptedException {
        expireCacheMap.put("token", UUID.randomUUID().toString());
        // 模拟线程等待
        TimeUnit.SECONDS.sleep(8);

        String token = expireCacheMap.get("token");
        long expiration = expireCacheMap.getExpiration("token");
        long expectedExpiration = expireCacheMap.getExpectedExpiration("token");
        log.info("token ===> " + token);
        log.info("设置的过期时间 ===> " + expiration / 1000);
        log.info("剩余过期时间 ===> " + expectedExpiration / 1000);

        // 模拟线程等待
        TimeUnit.SECONDS.sleep(10);
        log.info("token ===> " + expireCacheMap.get("token"));

        TimeUnit.SECONDS.sleep(60);
    }

说明一下:

  • maxSize:Map存储的最大值,类似队列,容量固定,当操作map容量超出限制时,最开始的元素就会依次过期,只保留最新的;
  • expirationPolicy:过期策略,包括 ExpirationPolicy.ACCESSED 和 ExpirationPolicy.CREATED 两种;
    • 1)ExpirationPolicy.ACCESSED :每进行一次访问,过期时间就会自动清零,重新计算;
    • 2)ExpirationPolicy.CREATED:在过期时间内重新 put 值的话,过期时间会清理,重新计算;默认策略。

在这里插入图片描述

2、单个元素设置过期时间

    /**
     * 构建后给单个元素设置过期时间
     */
    private final static ExpiringMap<String, String> expireCacheMap2 = ExpiringMap.builder()
            // 设置最大值,添加第11个entry时,会导致第1个立马过期(即使没到过期时间)。默认 Integer.MAX_VALUE
            .maxSize(10)
            // 允许 Map 元素具有各自的到期时间,并允许更改到期时间。
            .variableExpiration()
            .asyncExpirationListener((key, value) -> {
                log.info("expireCacheMap2 key数据被删除了 -> key={}, value={}", key, value);
            })
            //设置 Map 的过期策略
            .expirationPolicy(ExpirationPolicy.ACCESSED)
            .build();

    public static void main(String[] args) throws InterruptedException {
        expireCacheMap2.put("token", UUID.randomUUID().toString(), ExpirationPolicy.ACCESSED, 30, TimeUnit.SECONDS);
        expireCacheMap2.put("token2", UUID.randomUUID().toString(), ExpirationPolicy.CREATED, 30, TimeUnit.SECONDS);
        // 模拟线程等待
        TimeUnit.SECONDS.sleep(10);

        log.info("token ===> " + expireCacheMap2.get("token"));
        log.info("设置的过期时间 ===> " + expireCacheMap2.getExpiration("token") / 1000);
        log.info("剩余过期时间 ===> " + expireCacheMap2.getExpectedExpiration("token") / 1000);

        log.info("token2 ===> " + expireCacheMap2.get("token2"));
        log.info("设置的过期时间 ===> " + expireCacheMap2.getExpiration("token2") / 1000);
        log.info("剩余过期时间 ===> " + expireCacheMap2.getExpectedExpiration("token2") / 1000);

        // 模拟线程等待
        TimeUnit.SECONDS.sleep(20);
        log.info("token ===> " + expireCacheMap2.get("token"));
        log.info("token2 ===> " + expireCacheMap2.get("token2"));

        TimeUnit.SECONDS.sleep(60);
    }

说明一下:

  • variableExpiration:允许 Map 元素具有各自的到期时间,并允许更改到期时间。也就是说可以设置单个元素的过期时间。

在这里插入图片描述

3、设置懒加载

@Data
public class StudentDTO implements Serializable {
    private static final long serialVersionUID = 3220190006057051497L;

    private String name;
    private Integer age;
}
    /**
     * 构建后给元素设置懒加载。适合value为引用对象
     */
    private final static ExpiringMap<String, StudentDTO> expireCacheMap3 = ExpiringMap.builder()
            .expiration(10, TimeUnit.SECONDS)
            //设置懒加载
            .entryLoader(key -> new StudentDTO())
            .expirationPolicy(ExpirationPolicy.ACCESSED)
            .build();

    private final static ExpirationListener<String, StudentDTO> expirationListener = (key, value) -> log.info("expireCacheMap3 key数据被删除了 -> key={}, value={}", key, value);


    public static void main(String[] args) throws InterruptedException {
        // 构建时设置过期监听,或者添加过期监听。
        expireCacheMap3.addExpirationListener(expirationListener);

        // 使用懒加载时,可以不用去 put 对象,在调用 get 方法时会自动创建对象
        StudentDTO studentDTO1 = expireCacheMap3.get("studentDTO1");
        studentDTO1.setName("赵云");
        studentDTO1.setAge(17);
        log.info("studentDTO1 ===> " + expireCacheMap3.get("studentDTO1"));
        TimeUnit.SECONDS.sleep(3);
        log.info("token ===> " + expireCacheMap3.get("studentDTO1"));
        log.info("设置的过期时间 ===> " + expireCacheMap3.getExpiration("studentDTO1") / 1000);
        log.info("剩余过期时间 ===> " + expireCacheMap3.getExpectedExpiration("studentDTO1") / 1000);

        // 使用懒加载时,可以不用去 put 对象,在调用 get 方法时会自动创建对象
        StudentDTO studentDTO2 = expireCacheMap3.get("studentDTO2");
        studentDTO2.setName("赵子龙");
        studentDTO2.setAge(18);
        log.info("studentDTO2 ===> " + expireCacheMap3.get("studentDTO2"));
        TimeUnit.SECONDS.sleep(3);
        log.info("token ===> " + expireCacheMap3.get("studentDTO2"));
        log.info("设置的过期时间 ===> " + expireCacheMap3.getExpiration("studentDTO2") / 1000);
        log.info("剩余过期时间 ===> " + expireCacheMap3.getExpectedExpiration("studentDTO2") / 1000);

        // 模拟线程等待
        TimeUnit.SECONDS.sleep(20);
        log.info("studentDTO1 ===> " + expireCacheMap2.get("studentDTO1"));
        log.info("studentDTO2 ===> " + expireCacheMap2.get("studentDTO2"));

        TimeUnit.SECONDS.sleep(60);
    }

在这里插入图片描述

– 求知若饥,虚心若愚。

  • 29
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值