1. 程式人生 > 程式設計 >Java快取Map設定過期時間實現解析

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。