本文主要介绍了Java如何设置过期时间的map的几种方法,常见的解决方法有:ExpiringMap、LoadingCache及基于HashMap的封装三种,下面就详细的介绍一下,感兴趣的可以了解下
目录
一、技术背景
二、技术效果
三、ExpiringMap
3.1功能简介
3.2源码
3.3示例
四、LoadingCache
4.1功能简介
4.2示例
4.3移除机制
4.4其他
五、HashMap的封装

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

但是很多时候如果项目比较简单,就没有必要为了使用缓存而专门引入Redis等等中间件来加重系统的复杂性。那么Java本身有没有好用的轻量级的缓存组件呢。
答案当然是有喽,而且方法不止一种。常见的解决方法有:ExpiringMap、LoadingCache及基于HashMap的封装三种。

二、技术效果
实现缓存的常见功能,如过时删除策略
热点数据预热

三、ExpiringMap

3.1 功能简介
可设置Map中的Entry在一段时间后自动过期。
可设置Map最大容纳值,当到达Maximum size后,再次插入值会导致Map中的第一个值过期。
可添加监听事件,在监听到Entry过期时调度监听函数。
可以设置懒加载,在调用get()方法时创建对象。

3.2 源码
github地址

3.3 示例
添加依赖(Maven)

  1. <dependency>
  2. <groupId>net.jodah</groupId>
  3. <artifactId>expiringmap</artifactId>
  4. <version>0.5.8</version>
  5. </dependency>

示例源码

  1. public class ExpiringMapApp {
  2. public static void main(String[] args) {
  3. // maxSize: 设置最大值,添加第11个entry时,会导致第1个立马过期(即使没到过期时间)
  4. // expiration:设置每个key有效时间10s, 如果key不设置过期时间,key永久有效。
  5. // variableExpiration: 允许更新过期时间值,如果不设置variableExpiration,不允许后面更改过期时间,一旦执行更改过期时间操作会抛异常UnsupportedOperationException
  6. // policy:
  7. // CREATED: 只在put和replace方法清零过期时间
  8. // ACCESSED: 在CREATED策略基础上增加, 在还没过期时get方法清零过期时间。
  9. // 清零过期时间也就是重置过期时间,重新计算过期时间.
  10. ExpiringMap<String, String> map = ExpiringMap.builder()
  11. .maxSize(10)
  12. .expiration(10, TimeUnit.SECONDS)
  13. .variableExpiration().expirationPolicy(ExpirationPolicy.CREATED).build();
  14. map.put("token", "lkj2412lj1412412nmlkjl2n34l23n4");
  15. map.put("name", "管理员", 20000, TimeUnit.SECONDS);
  16. // 模拟线程等待...
  17. try {
  18. Thread.sleep(15000);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println("token ===> " + map.get("token"));
  23. System.out.println("name ===> " + map.get("name"));
  24. // 注意: 在创建map时,指定的那些参数如过期时间和过期策略都是全局的, 对map中添加的每一个entry都适用.
  25. // 在put一个entry键值对时可以对当前entry 单独设置 过期时间、过期策略,只对当前这个entry有效.
  26. }
  27. }

运行结果

token ===> null
name ===> 管理员

注意
在创建map时,指定的那些参数如过期时间和过期策略都是全局的, 对map中添加的每一个entry都适用。
在put一个entry键值对时可以对当前entry 单独设置 过期时间、过期策略,只对当前这个entry有效.
四、LoadingCache
4.1 功能简介
Google开源出来的一个线程安全的本地缓存解决方案。

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

4.2 示例
源码

  1. public class LoadingCacheApp {
  2. public static void main(String[] args) throws Exception {
  3. // maximumSize: 缓存池大小,在缓存项接近该大小时, Guava开始回收旧的缓存项
  4. // expireAfterAccess: 设置时间对象没有被读/写访问则对象从内存中删除(在另外的线程里面不定期维护)
  5. // removalListener: 移除监听器,缓存项被移除时会触发的钩子
  6. // recordStats: 开启Guava Cache的统计功能
  7. LoadingCache<String, String> cache = CacheBuilder.newBuilder()
  8. .maximumSize(100)
  9. .expireAfterAccess(10, TimeUnit.SECONDS)
  10. .removalListener(new RemovalListener<String, String>() {
  11. @Override
  12. public void onRemoval(RemovalNotification<String, String> removalNotification) {
  13. System.out.println("过时删除的钩子触发了... key ===> " + removalNotification.getKey());
  14. }
  15. })
  16. .recordStats()
  17. .build(new CacheLoader<String, String>() {
  18. // 处理缓存键不存在缓存值时的处理逻辑
  19. @Override
  20. public String load(String key) throws Exception {
  21. return "不存在的key";
  22. }
  23. });
  24. cache.put("name", "小明");
  25. cache.put("pwd", "112345");
  26. // 模拟线程等待...
  27. try {
  28. Thread.sleep(15000);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. System.out.println("token ===> " + cache.get("name"));
  33. System.out.println("name ===> " + cache.get("pwd"));
  34. }
  35. }

运行结果

过时删除的钩子触发了… key ===> name
token ===> 不存在的key
过时删除的钩子触发了… key ===> pwd
name ===> 不存在的key
4.3 移除机制
guava做cache时候数据的移除分为被动移除和主动移除两种。

被动移除

基于大小的移除:数量达到指定大小,会把不常用的键值移除
基于时间的移除:expireAfterAccess(long, TimeUnit) 根据某个键值对最后一次访问之后多少时间后移除。expireAfterWrite(long, TimeUnit) 根据某个键值对被创建或值被替换后多少时间移除
基于引用的移除:主要是基于java的垃圾回收机制,根据键或者值的引用关系决定移除
主动移除

单独移除:Cache.invalidate(key)
批量移除:Cache.invalidateAll(keys)
移除所有:Cache.invalidateAll()
如果配置了移除监听器RemovalListener,则在所有移除的动作时会同步执行该listener下的逻辑。

如需改成异步,使用:RemovalListeners.asynchronous(RemovalListener, Executor).

4.4 其他
在put操作之前,如果已经有该键值,会先触发removalListener移除监听器,再添加
配置了expireAfterAccess和expireAfterWrite,但在指定时间后没有被移除。
删除策略逻辑:
CacheBuilder构建的缓存不会在特定时间自动执行清理和回收工作,也不会在某个缓存项过期后马上清理,它不会启动一个线程来进行缓存维护,因为首先线程相对较重,其次某些环境限制线程的创建。

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

五、HashMap的封装
我们可以参考上面两个工具包的思路,自己封装一个可以设置过时时间的HashMap来实现我们想要的效果。
以上就是本文全部内容,希望能给大家带来帮助。

更多相关文章

  1. Android(安卓)自定义Animation,使用Camera对View组件作三维变换
  2. Android图片上传的工具类
  3. Android(Java):长时间未登录提醒
  4. 【HR小时制】如何获取Android系统时间的小时制
  5. Android(安卓)获取时间实时更新UI
  6. android时间控件DatePicker使用实例
  7. Android时间倒计时的简单实线
  8. android闹钟(三):实现时钟功能
  9. Android时间选择器

随机推荐

  1. Java Dao层的作用
  2. PHP魔术方法之__clone详解(代码实例)
  3. PHP面向对象之多态详解(代码实例)
  4. PHP 有趣的经典算法
  5. PHP魔术方法之__iset,__unset详解(代码实
  6. php中session时间设置浅析
  7. PHP 与 Go 的语法区别
  8. PHP面向对象之接口详解(代码实例)
  9. PHP中正则表达式详解(代码实例)
  10. 面向对象中什么是封装