Caffeine的底层数据存储采用ConcurrentHashMap。
Caffeine是Spring 5默认支持的Cache,可见Spring对它的看重,Spring抛弃Guava转向了Caffeine。
Caffeine可以看作是Guava Cache的增强版,采用了一种结合LRU、LFU优点的算法:W-TinyLFU,在性能上有明显的优越性
com.github.ben-manes.caffeine caffeine 2.9.3
- public static void main(String[] args) throws InterruptedException {
- Cache
- // 初始容量
- .initialCapacity(100)
- // 最大缓存数
- .maximumSize(1000)
- // 过期时间
- .expireAfterAccess(5, TimeUnit.SECONDS)
- // 失效时触发,不会自动触发
- .removalListener((k,v,removeCause)->{
- System.out.println("--------------"+k);
- }).recordStats()
- .build();
- cache.put("a","b");
- System.out.println(cache.getIfPresent("a")); // b
- Thread.sleep(6000);
- // cache.invalidate("a"); // 触发removalListener
- System.out.println(cache.getIfPresent("a")); // null
- System.out.println(cache.get("a", k -> { // 不存在则填充值,之前的过期触发removalListener
- System.out.println("进入get");
- return k;
- }));
- }
Guava是Google团队开源的一款 Java 核心增强库,包含集合、并发原语、缓存、IO、反射等工具箱,性能和稳定性上都有保障,应用十分广泛。Guava Cache支持很多特性:
- 支持最大容量限制
- 支持两种过期删除策略(插入时间和访问时间)
- 支持简单的统计功能
- 基于LRU算法实现
com.google.guava guava 18.0
- public static void main(String[] args) throws Exception {
- LoadingCache
loadingCache - //CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
- = CacheBuilder.newBuilder()
- //设置并发级别为8,并发级别是指可以同时写缓存的线程数
- // .concurrencyLevel(8)
- //设置写缓存后8秒钟过期
- .expireAfterWrite(2, TimeUnit.SECONDS)
- // .expireAfterAccess(10, TimeUnit.MINUTES)//设置时间对象没有被读/写访问则对象从内存中删除
- //设置缓存容器的初始容量为10
- .initialCapacity(10)
- //设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
- .maximumSize(100)
- //设置要统计缓存的命中率
- .recordStats()
- //设置缓存的移除通知
- .removalListener((all)->{
- System.out.println("移除key:"+all.getKey());
- })
- //build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
- .build(
- new CacheLoader
() { - @Override
- public String load(String s) throws Exception {
- return "b";
- }
- }
- );
- String a = loadingCache.get("a"); // 不存在key为“a”则触发load() return "b";
- // loadingCache.invalidate("a"); // 手动失效key,触发removalListener, System.out.println("移除key:"+all.getKey());
- TimeUnit.SECONDS.sleep(3);
- loadingCache.get("a"); // 过期触发removalListener, System.out.println("移除key:"+all.getKey());
- }
其缓存的数据可以放在内存里面,也可以放在硬盘上。
ehcache的核心是cacheManager,cacheManager是用来管理cache(缓存)的。
一个应用下可以有多个cacheManager,而一个cacheManager下又可以有多个cache
cache内部保存的是一个的element,一个element中保存的是一个key和value的配对。
1、快速
2、简单
3、多种缓存策略
4、缓存数据有两级:内存和磁盘,因此无须担心容量问题
5、缓存数据会在虚拟机重启的过程中写入磁盘
6、可通过RMI、可插入APi等方式进行分布式缓存
7、具有缓存和缓存管理器的监听接口
8、支持多缓存管理器示例,以及一个实例的多个缓存区域
9、提供hibernate的缓存实现
同Caffeine和Guava Cache相比,Encache的功能更加丰富,扩展性更强
ehcache也有缓存共享方案,不过是通过RMI或者Jgroup多播方式进行广播缓存通知更新,缓存共享复杂,维护不方便;简单的共享可以,但涉及缓存恢复、大数据缓存,则不适合。
org.ehcache ehcache 3.9.0
- public static void main(String[] args) {
- CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
- .withCache("cache",
- CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(20))) 声明一个容量为20的堆内缓存
- .build(true);
- // cacheManager.init(); // true
- // Cache
cache = - // cacheManager.getCache("cache", Long.class, String.class);
- // cache.put();
- // cache.get()
- Cache
myCache = cacheManager.createCache("myCache", - CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))
- .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(20)))//设置过期 可自定义过期策略
- );
-
- myCache.put(1L, "one!");
- String value = myCache.get(1L);
- cacheManager.removeCache("cache");
- cacheManager.close();
- }
jdk自带,根据合适场景使用
WeakHashMap.Entry 继承了WeakReference。
弱引用的生存期特别短。垃圾回收的时候,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。
我使用的场景理由:过期自动触发移除事件
net.jodah expiringmap 0.5.8
- public static void main(String[] args) throws InterruptedException {
- ExpiringMap
map = ExpiringMap.builder() - .expirationPolicy(ExpirationPolicy.CREATED)
- .expirationListener((k,v)->{
- System.out.println(k);
- System.out.println(v);
- })
- .maxSize(123)
- .expiration(2, TimeUnit.SECONDS)
- .build();
-
- // Expires after 30 seconds or as soon as a 124th element is added and this is the next one to expire based on the expiration policy
- map.put("connection", "1");
- TimeUnit.SECONDS.sleep(3);
- }
- // 可以使用ExpiringMap.builder().variableExpiration().build();然后搭配map,put(k,v, ExpirationPolicy.ACCESSED, 5, TimeUnit.MINUTES)为单个元素设置过期时间,不推荐