这篇文章主要介绍了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。(高并发的时候会有问题,HashMap不支持高并发,换成ConcurrentHashMap也不行,因为是操作两个Map,不具备原子性,最简单的办法就是增加synchronized,也要看怎么加才能满足高并发,又不是那么影响效率

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"));
  }
}

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

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐