java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java设置Map过期时间

Java设置Map过期时间的的几种方法举例详解

作者:虞泽

本文详细介绍了Java中使用轻量级缓存组件ExpiringMap以及Guava的LoadingCache缓存机制,ExpiringMap提供了Map自动过期、监听事件等功能,而LoadingCache提供了缓存回收、数据加载等高级功能,两者为Java项目提供了有效的数据管理和缓存解决方案,需要的朋友可以参考下

一、技术背景

在实际的项目开发中,我们经常会使用到缓存中间件(如redis、MemCache等)来帮助我们提高系统的可用性和健壮性。

但是很多时候如果项目比较简单,就没有必要为了使用缓存而专门引入Redis等等中间件来加重系统的复杂性。那么使用Java本身自己的轻量级的缓存组件就是完美解决方式。

二、技术效果

三、ExpiringMap

3.1 ExpiringMap简介

ExpiringMap具有高性能、低开销、零依赖、线程安全、使用 ConcurrentMap 的实现过期 entries 等优点。
功能包括不限于:

3.2 ExpiringMap使用

3.2.1 pom.xml 中添加依赖

        <!-- https://mvnrepository.com/artifact/net.jodah/expiringmap -->
        <dependency>
            <groupId>net.jodah</groupId>
            <artifactId>expiringmap</artifactId>
            <version>0.5.10</version>
        </dependency>

3.2.2 代码中使用

    /**
     * ① maxSize:Map存储的最大值,类似队列,容量固定,当操作map容量超出限制时,最开始的元素就会依次过期,只保留最新的;
     * ② expiration:过期时间;
     * ③ expirationListener:过期监听,当条目过期时,将同步调用过期侦听器,并且在侦听器完成之前,
     *  将阻止对映射的写入操作。还可以在单独的线程池中配置和调用异步过期侦听器,而不会阻塞映射操作;
     * ④ expirationPolicy:过期策略,包括 ExpirationPolicy.ACCESSED 和 ExpirationPolicy.CREATED 两种;
     *      1)ExpirationPolicy.ACCESSED :每进行一次访问,过期时间就会自动清零,重新计算;
     *      2)ExpirationPolicy.CREATED:在过期时间内重新 put 值的话,过期时间会清理,重新计算;
     * ⑤ variableExpiration:可变过期,条目可以具有单独可变的到期时间和策略:
     */
    public static  ExpiringMap<String, String> map = ExpiringMap.builder()
            .maxSize(1000)
            .expiration(2, TimeUnit.HOURS)
            .variableExpiration()
            .expirationPolicy(ExpirationPolicy.ACCESSED)
            .expirationListener((key, value) -> {
                System.out.println("SseEmitter已过期,key:"+ key);
            })
            .build();

3.2.3 参数说明

 ① maxSize:Map存储的最大值,类似队列,容量固定,当操作map容量超出限制时,最开始的元素就会依次过期,只保留最新的;
 ② expiration:过期时间;
 ③ expirationListener:过期监听,当条目过期时,将同步调用过期侦听器,并且在侦听器完成之前,
  将阻止对映射的写入操作。还可以在单独的线程池中配置和调用异步过期侦听器,而不会阻塞映射操作;
 ④ expirationPolicy:过期策略,包括 ExpirationPolicy.ACCESSED 和 ExpirationPolicy.CREATED 两种;
           1)ExpirationPolicy.ACCESSED :每进行一次访问,过期时间就会自动清零,重新计算;
           2)ExpirationPolicy.CREATED:在过期时间内重新 put 值的话,过期时间会清理,重新计算;
 ⑤ variableExpiration:可变过期,条目可以具有单独可变的到期时间和策略;

3.2.4 其他使用方式

		//为单个条目指定到期策略:
        map.put("1", "张三", ExpirationPolicy.CREATED);
        map.put("2", "李四", ExpirationPolicy.ACCESSED);

        //variableExpiration 可变过期 条目可以具有单独可变的到期时间和策略:
        map.put("3", "王五", ExpirationPolicy.ACCESSED, 5, TimeUnit.MINUTES);

        //过期时间和策略也可以即时更改:
        map.setExpiration("1", 5, TimeUnit.MINUTES);
        map.setExpirationPolicy("1", ExpirationPolicy.ACCESSED);

        //动态添加和删除过期侦听器:
        ExpirationListener<String, String> connectionCloser = (key, value) -> System.out.println(key+":"+value);
        //添加侦听器
        map.addExpirationListener(connectionCloser);
        //移除侦听器
        map.removeExpirationListener(connectionCloser);

        //设置懒加载
//        Map<String, String> stringMap = ExpiringMap.builder()
//                .expiration(10, TimeUnit.MINUTES)
//                .entryLoader(address -> address)
//                .build();
//        // 通过 EntryLoader 将值加载到map中
//        String value = stringMap.get("1");
//        System.out.println("value值:"+value);

        //获取条目的到期时间:单位:毫秒
        long expiration = map.getExpectedExpiration("1");
        System.out.println("距离过期时间还有:"+expiration+"毫秒");

        //重置条目的内部到期计时器:
        map.resetExpiration("1");

        //查看设置的过期时间
        map.getExpiration("1");
        System.out.println("设置的过期时间:"+map.getExpiration("1"));
        

测试结果

距离过期时间还有:299999毫秒
设置的过期时间:300000

四、Guava的LoadingCache

4.1 LoadingCache简介

做java的我们都知道Guava是一个编程工具类库,其中包含了很多高质量高性能的工具类和方法。其中,LoadingCache便是一个特别好用的功能,其背后的架构其实就是Guava cache,Guava Cache 是一个全内存的本地缓存实现,它提供了线程安全的实现机制,它可以加载缓存中不存在的数据,本质其实是一个键值对(key-value)的缓存,可以通过key获取到对应的缓存值value。
特点:提供缓存回收机制,监控缓存加载/命中情况,灵活强大的功能,简单易上手的api。

4.2 LoadingCache使用

4.2.1 pom.xml 中添加依赖

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

4.2.2 代码中使用

	public static LoadingCache<Long, User> userCache= CacheBuilder.newBuilder()
            // 缓存池大小,在缓存数量到达该大小时, 开始回收旧的数据
            .maximumSize(1000)
            // 设置时间60s对象没有被读/写访问则对象从内存中删除
            .expireAfterAccess(60, TimeUnit.SECONDS)
            // 设置缓存在写入之后 设定时间60s后失效
            .expireAfterWrite(60, TimeUnit.SECONDS)
            // 定时刷新,设置时间10s后,当有访问时会重新执行load方法重新加载
            .refreshAfterWrite(10, TimeUnit.SECONDS)
            // 移除监听器,缓存项被移除时会触发
            .removalListener(new RemovalListener() {
                @Override
                public void onRemoval(RemovalNotification rn) {
                    // 处理缓存键不存在缓存值时的处理逻辑
                    log.error(rn.getKey() + "remove");
                }
            })
            // 处理缓存键对应的缓存值不存在时的处理逻辑
            .build(new CacheLoader<Long, User>() {
                @Override
                public User load(Long id) {
                    return getById(id);
                }
    });
 
    public User getUser(Long id) {
        User user = userCache.get(id);
    }
 
    public ImmutableMap<Long, User > getAll(List<Long> ids) throws ExecutionException {
        return cache.getAll(ids);
    }

4.2.3 参数说明

 ① maximumSize:缓存的k-v最大数据,当总缓存的数据量达到这个值时,就会淘汰它认为不太用的一份数据,会使用LRU策略进行回收;
 ② expireAfterAccess:缓存项在给定时间内没有被读/写访问,则回收,这个策略主要是为了淘汰长时间不被访问的数据;
 ③ expireAfterWrite:缓存项在给定时间内没有被写访问(创建或覆盖),则回收, 防止旧数据被缓存过久;
 ④ refreshAfterWrite:缓存项在给定时间内没有被写访问(创建或覆盖),则刷新;
 ⑤ recordStats:开启Cache的状态统计(默认是开启的);

4.2.4 GET方法

  V get(K key, int hash, CacheLoader<? super K, V> loader) throws ExecutionException {
    try {
      if (count != 0) { // read-volatile
        ReferenceEntry<K, V> e = getEntry(key, hash);
        if (e != null) {
          long now = map.ticker.read();
          //检查entry是否符合expireAfterAccess淘汰策略
          V value = getLiveValue(e, now);
          // value是有效的 则返回
          if (value != null) {
            // 记录该值的最近访问时间
            recordRead(e, now);
            statsCounter.recordHits(1);
            // 内部实现了定时刷新,若未开启refreshAfterWrite则直接返回value
            return scheduleRefresh(e, key, hash, value, now, loader);
          }
          ValueReference<K, V> valueReference = e.getValueReference();
          // 如果有别的线程已经在load value,则等到其他线程完成后再取结果
          if (valueReference.isLoading()) {
            return waitForLoadingValue(e, key, valueReference);
          }
        }
      }
 
      // 如果没拿到有效的value,则执行加载逻辑;
      return lockedGetOrLoad(key, hash, loader);
    } catch (ExecutionException ee) {
      ...
    } finally {
      postReadCleanup();
    }
  }

4.2.5 Load方法

@GwtCompatible(emulated = true)
public abstract class CacheLoader<K, V> {
 
  public abstract V load(K key) throws Exception;
 
}

4.3 移除机制

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

如果配置了移除监听器RemovalListener,则在所有移除的动作时会同步执行该listener下的逻辑。
如需改成异步,使用:RemovalListeners.asynchronous(RemovalListener, Executor)。

4.4 其他

它会在写操作时顺带做少量的维护工作,或者偶尔在读操作时做。当然,也可以创建自己的维护线程,以固定的时间间隔调用Cache.cleanUp()。

总结

到此这篇关于Java设置Map过期时间的的几种方法的文章就介绍到这了,更多相关Java设置Map过期时间内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文