Java缓存Map设置过期时间实现解析

 更新时间:2019年12月23日 08:58:09   作者:1024。  
这篇文章主要介绍了Java缓存Map设置过期时间实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

这篇文章主要介绍了Java缓存Map设置过期时间实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

前言

最近项目需求需要一个类似于redis可以设置过期时间的K,V存储方式。项目前期暂时不引进redis,暂时用java内存代替。

解决方案

1. ExpiringMap

功能简介 :

1.可设置Map中的Entry在一段时间后自动过期。

2.可设置Map最大容纳值,当到达Maximum size后,再次插入值会导致Map中的第一个值过期。

3.可添加监听事件,在监听到Entry过期时调度监听函数。

4.可以设置懒加载,在调用get()方法时创建对象。

github地址:https://github.com/jhalterman/expiringmap/

maven添加依赖即可使用

<dependency> 
  <groupId>net.jodah</groupId> 
  <artifactId>expiringmap</artifactId> 
  <version>0.5.8</version> 
</dependency> 
public static void main(String[] args) throws InterruptedException {
    ExpiringMap<String,String> map = ExpiringMap.builder()
        .maxSize(100)
        .expiration(1, TimeUnit.SECONDS)
        .expirationPolicy(ExpirationPolicy.ACCESSED)
        .variableExpiration()
        .build();
    map.put("test","test123");
    Thread.sleep(500);
    String test= map.get("test");
    System.err.println(test);
 }

2.Guava - LoadingCache

Google开源出来的一个线程安全的本地缓存解决方案。

特点:提供缓存回收机制,监控缓存加载/命中情况,灵活强大的功能,简单易上手的api

但是该cache不会在特定时间准时回收键值,所以不适用于我当前的业务场景。

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

 3. ExpiryMap

这是网上某位大佬自己封装的map,继承至HashMap,重写了所有对外的方法,对每个key值都设置了有效期。

我在其基础上增加了使用单例模式获取map。

import java.util.*;

/**
 * @Title: ExpiryMap 可以设置过期时间的Map
 * @description ExpiryMap继承至HashMap 重写了所有对外的方法,对每个key值都设置了有效期
 * @Author: xx
 * @Version: 1.0
 */
public class ExpiryMap<K, V> extends HashMap<K, V> {

  private static final long serialVersionUID = 1L;

  /**
   * default expiry time 2s
   */
  private long EXPIRY = 1000 * 2;

  private HashMap<K, Long> expiryMap = new HashMap<>();

  /** 缓存实例对象 */
  private volatile static ExpiryMap<String, String> SameUrlMap;

  /**
   * 采用单例模式获取实例
   * @return
   */
  public static ExpiryMap getInstance() {
    //第一次判空,提高效率
    if (null == SameUrlMap) {
      //保证线程安全
      synchronized (ExpiryMap.class) {
        //第二次判空,保证单例对象的唯一性,防止第一次有多个线程进入第一个if判断
        if (null == SameUrlMap) {
          SameUrlMap = new ExpiryMap<>();
        }
      }
    }
    return SameUrlMap;
  }

  public ExpiryMap(){
    super();
  }

  public ExpiryMap(long defaultExpiryTime){
    this(1 << 4, defaultExpiryTime);
  }

  public ExpiryMap(int initialCapacity, long defaultExpiryTime){
    super(initialCapacity);
    this.EXPIRY = defaultExpiryTime;
  }

  @Override
  public V put(K key, V value) {
    expiryMap.put(key, System.currentTimeMillis() + EXPIRY);
    return super.put(key, value);
  }

  @Override
  public boolean containsKey(Object key) {
    return !checkExpiry(key, true) && super.containsKey(key);
  }
  /**
   * @param key
   * @param value
   * @param expiryTime 键值对有效期 毫秒
   * @return
   */
  public V put(K key, V value, long expiryTime) {
    expiryMap.put(key, System.currentTimeMillis() + expiryTime);
    return super.put(key, value);
  }

  @Override
  public int size() {
    return entrySet().size();
  }

  @Override
  public boolean isEmpty() {
    return entrySet().size() == 0;
  }

  @Override
  public boolean containsValue(Object value) {
    if (value == null) {
      return Boolean.FALSE;
    }
    Set<Entry<K, V>> set = super.entrySet();
    Iterator<Entry<K, V>> iterator = set.iterator();
    while (iterator.hasNext()) {
      java.util.Map.Entry<K, V> entry = iterator.next();
      if(value.equals(entry.getValue())){
        if(checkExpiry(entry.getKey(), false)) {
          iterator.remove();
          return Boolean.FALSE;
        }else {
          return Boolean.TRUE;
        }
      }
    }
    return Boolean.FALSE;
  }

  @Override
  public Collection<V> values() {

    Collection<V> values = super.values();

    if(values == null || values.size() < 1) {
      return values;
    }

    Iterator<V> iterator = values.iterator();

    while (iterator.hasNext()) {
      V next = iterator.next();
      if(!containsValue(next)) {
        iterator.remove();
      }
    }
    return values;
  }

  @Override
  public V get(Object key) {
    if (key == null) {
      return null;
    }
    if(checkExpiry(key, true)) {
      return null;
    }
    return super.get(key);
  }
  /**
   *
   * @Description: 是否过期
   * @param key
   * @return null:不存在或key为null -1:过期 存在且没过期返回value 因为过期的不是实时删除,所以稍微有点作用
   */
  public Object isInvalid(Object key) {
    if (key == null) {
      return null;
    }
    if(!expiryMap.containsKey(key)){
      return null;
    }
    long expiryTime = expiryMap.get(key);

    boolean flag = System.currentTimeMillis() > expiryTime;

    if(flag){
      super.remove(key);
      expiryMap.remove(key);
      return -1;
    }
    return super.get(key);
  }

  @Override
  public void putAll(Map<? extends K, ? extends V> m) {
    for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
      expiryMap.put(e.getKey(), System.currentTimeMillis() + EXPIRY);
    }
    super.putAll(m);
  }

  @Override
  public Set<Map.Entry<K,V>> entrySet() {
    Set<java.util.Map.Entry<K, V>> set = super.entrySet();
    Iterator<java.util.Map.Entry<K, V>> iterator = set.iterator();
    while (iterator.hasNext()) {
      java.util.Map.Entry<K, V> entry = iterator.next();
      if(checkExpiry(entry.getKey(), false)) {
        iterator.remove();
      }
    }

    return set;
  }
  /**
   *
   * @Description: 是否过期
   * @param expiryTime true 过期
   * @param isRemoveSuper true super删除
   * @return
   */
  private boolean checkExpiry(Object key, boolean isRemoveSuper){

    if(!expiryMap.containsKey(key)){
      return Boolean.FALSE;
    }
    long expiryTime = expiryMap.get(key);

    boolean flag = System.currentTimeMillis() > expiryTime;

    if(flag){
      if(isRemoveSuper) {
        super.remove(key);
      }
      expiryMap.remove(key);
    }
    return flag;
  }

  public static void main(String[] args) throws InterruptedException {
    ExpiryMap<String, String> map = new ExpiryMap<>();
    map.put("test", "xxx");
    map.put("test2", "ankang", 5000);
    System.out.println("test==" + map.get("test"));
    Thread.sleep(3000);
    System.out.println("test==" + map.get("test"));
    System.out.println("test2==" + map.get("test2"));
    Thread.sleep(3000);
    System.out.println("test2==" + map.get("test2"));
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Spring事务失效的一种原因关于this调用的问题

    Spring事务失效的一种原因关于this调用的问题

    这篇文章主要介绍了Spring事务失效的一种原因关于this调用的问题,本文给大家分享问题原因及解决办法,通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2021-10-10
  • 利用MultipartFile实现文件上传功能

    利用MultipartFile实现文件上传功能

    这篇文章主要为大家详细介绍了利用MultipartFile实现文件上传功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • java N皇后实现问题解析

    java N皇后实现问题解析

    将 n 个皇后摆放在一个 n x n 的棋盘上,使得每一个皇后都无法攻击到其他皇后,N皇后问题是一个典型的约束求解问题,利用递归机制,可以很快的得到结果,本文将详细介绍,需要了解的朋友可以参考下
    2012-11-11
  • Java接口幂等性设计原理解析

    Java接口幂等性设计原理解析

    这篇文章主要介绍了Java接口幂等性设计原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • 详解Java 中 RMI 的使用

    详解Java 中 RMI 的使用

    这篇文章主要介绍了Java 中 RMI 的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-05-05
  • Java实现简单推箱子游戏

    Java实现简单推箱子游戏

    这篇文章主要为大家详细介绍了Java实现推箱子游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06
  • 关于@Bean的使用方式

    关于@Bean的使用方式

    这篇文章主要介绍了关于@Bean的使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • Java多线程基础

    Java多线程基础

    这篇文章主要介绍Java多线程基础,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,多线程指在单个程序中可以同时运行多个不同的线程执行不同的任务,下面来学习具体的详细内容
    2021-10-10
  • 在mybatis中使用mapper进行if条件判断

    在mybatis中使用mapper进行if条件判断

    这篇文章主要介绍了在mybatis中使用mapper进行if条件判断,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • Java排序算法总结之归并排序

    Java排序算法总结之归并排序

    这篇文章主要介绍了Java排序算法总结之归并排序,较为详细的分析了归并排序的原理与java实现技巧,需要的朋友可以参考下
    2015-05-05

最新评论