可以设置过期时间的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不会在特定时间准时回收键值,所以不适用于我当前的业务场景。

  详细描述介绍看我的另外一篇博客:https://www.cnblogs.com/xhq1024/p/11174775.html

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

    3. ExpiryMap

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

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

  1 import java.util.*;
  2 
  3 /**
  4  * @Title: ExpiryMap 可以设置过期时间的Map
  5  * @description ExpiryMap继承至HashMap 重写了所有对外的方法,对每个key值都设置了有效期
  6  * @Author: xx
  7  * @Version: 1.0
  8  */
  9 public class ExpiryMap<K, V> extends HashMap<K, V> {
 10 
 11     private static final long serialVersionUID = 1L;
 12 
 13     /**
 14      * default expiry time 2s
 15      */
 16     private long EXPIRY = 1000 * 2;
 17 
 18     private HashMap<K, Long> expiryMap = new HashMap<>();
 19 
 20     /**  缓存实例对象 */
 21     private volatile static ExpiryMap<String, String> SameUrlMap;
 22 
 23     /**
 24      * 采用单例模式获取实例
 25      * @return
 26      */
 27     public static ExpiryMap getInstance() {
 28         //第一次判空,提高效率
 29         if (null == SameUrlMap) {
 30             //保证线程安全
 31             synchronized (ExpiryMap.class) {
 32                 //第二次判空,保证单例对象的唯一性,防止第一次有多个线程进入第一个if判断
 33                 if (null == SameUrlMap) {
 34                     SameUrlMap = new ExpiryMap<>();
 35                 }
 36             }
 37         }
 38         return SameUrlMap;
 39     }
 40 
 41     public ExpiryMap(){
 42         super();
 43     }
 44 
 45     public ExpiryMap(long defaultExpiryTime){
 46         this(1 << 4, defaultExpiryTime);
 47     }
 48 
 49     public ExpiryMap(int initialCapacity, long defaultExpiryTime){
 50         super(initialCapacity);
 51         this.EXPIRY = defaultExpiryTime;
 52     }
 53 
 54     @Override
 55     public V put(K key, V value) {
 56         expiryMap.put(key, System.currentTimeMillis() + EXPIRY);
 57         return super.put(key, value);
 58     }
 59 
 60     @Override
 61     public boolean containsKey(Object key) {
 62         return !checkExpiry(key, true) && super.containsKey(key);
 63     }
 64     /**
 65      * @param key
 66      * @param value
 67      * @param expiryTime 键值对有效期 毫秒
 68      * @return
 69      */
 70     public V put(K key, V value, long expiryTime) {
 71         expiryMap.put(key, System.currentTimeMillis() + expiryTime);
 72         return super.put(key, value);
 73     }
 74 
 75     @Override
 76     public int size() {
 77         return entrySet().size();
 78     }
 79 
 80     @Override
 81     public boolean isEmpty() {
 82         return entrySet().size() == 0;
 83     }
 84 
 85     @Override
 86     public boolean containsValue(Object value) {
 87         if (value == null) {
 88             return Boolean.FALSE;
 89         }
 90         Set<Entry<K, V>> set = super.entrySet();
 91         Iterator<Entry<K, V>> iterator = set.iterator();
 92         while (iterator.hasNext()) {
 93             java.util.Map.Entry<K, V> entry = iterator.next();
 94             if(value.equals(entry.getValue())){
 95                 if(checkExpiry(entry.getKey(), false)) {
 96                     iterator.remove();
 97                     return Boolean.FALSE;
 98                 }else {
 99                     return Boolean.TRUE;
100                 }
101             }
102         }
103         return Boolean.FALSE;
104     }
105 
106     @Override
107     public Collection<V> values() {
108 
109         Collection<V> values = super.values();
110 
111         if(values == null || values.size() < 1) {
112             return values;
113         }
114 
115         Iterator<V> iterator = values.iterator();
116 
117         while (iterator.hasNext()) {
118             V next = iterator.next();
119             if(!containsValue(next)) {
120                 iterator.remove();
121             }
122         }
123         return values;
124     }
125 
126     @Override
127     public V get(Object key) {
128         if (key == null) {
129             return null;
130         }
131         if(checkExpiry(key, true)) {
132             return null;
133         }
134         return super.get(key);
135     }
136     /**
137      *
138      * @Description: 是否过期
139      * @param key
140      * @return null:不存在或key为null -1:过期  存在且没过期返回value 因为过期的不是实时删除,所以稍微有点作用
141      */
142     public Object isInvalid(Object key) {
143         if (key == null) {
144             return null;
145         }
146         if(!expiryMap.containsKey(key)){
147             return null;
148         }
149         long expiryTime = expiryMap.get(key);
150 
151         boolean flag = System.currentTimeMillis() > expiryTime;
152 
153         if(flag){
154             super.remove(key);
155             expiryMap.remove(key);
156             return -1;
157         }
158         return super.get(key);
159     }
160 
161     @Override
162     public void putAll(Map<? extends K, ? extends V> m) {
163         for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
164             expiryMap.put(e.getKey(), System.currentTimeMillis() + EXPIRY);
165         }
166         super.putAll(m);
167     }
168 
169     @Override
170     public Set<Map.Entry<K,V>> entrySet() {
171         Set<java.util.Map.Entry<K, V>> set = super.entrySet();
172         Iterator<java.util.Map.Entry<K, V>> iterator = set.iterator();
173         while (iterator.hasNext()) {
174             java.util.Map.Entry<K, V> entry = iterator.next();
175             if(checkExpiry(entry.getKey(), false)) {
176                 iterator.remove();
177             }
178         }
179 
180         return set;
181     }
182     /**
183      *
184      * @Description: 是否过期
185      * @param expiryTime true 过期
186      * @param isRemoveSuper true super删除
187      * @return
188      */
189     private boolean checkExpiry(Object key, boolean isRemoveSuper){
190 
191         if(!expiryMap.containsKey(key)){
192             return Boolean.FALSE;
193         }
194         long expiryTime = expiryMap.get(key);
195 
196         boolean flag = System.currentTimeMillis() > expiryTime;
197 
198         if(flag){
199             if(isRemoveSuper) {
200                 super.remove(key);
201             }
202             expiryMap.remove(key);
203         }
204         return flag;
205     }
206 
207     public static void main(String[] args) throws InterruptedException {
208         ExpiryMap<String, String> map = new ExpiryMap<>();
209         map.put("test", "xxx");
210         map.put("test2", "ankang", 5000);
211         System.out.println("test==" + map.get("test"));
212         Thread.sleep(3000);
213         System.out.println("test==" + map.get("test"));
214         System.out.println("test2==" + map.get("test2"));
215         Thread.sleep(3000);
216         System.out.println("test2==" + map.get("test2"));
217     }
218 }

附上ExpiryMap原文地址:https://blog.csdn.net/u011534095/article/details/54091337

 

posted @ 2019-07-01 18:05  猿了个码  阅读(20206)  评论(2编辑  收藏  举报