1、HashMap
1.1 特点
- K-V 形式
- 线程不安全
- 查询效率快
通过线程不安全的特点,表现了 HashMap 的应用场景局限于单线程(没有线程并发问题的场景)
1.2 基础操作
Map<Object,Object> map=new HashMap<>();
// 添加元素
map.put("key","value");
// 获取元素
map.get("key");
// 删除元素
map.remove("key");
2、ConcurrentHashMap
为了针对无法应用 HashMap 的多线程环境,可以采用并发安全的集合类 ConcurrentHashMap。
对于 HashMap 的并发安全类有许多
- Hashtable
- Collections.synchronizedMap()
- ConcurrentHashMap
concurrentHashMap 在 JDK 采用的 CAS+synchronized,性能最好
Map<Object,Object> map=new ConcurrentHashMap<>();
// 添加元素
map.put("key","value");
// 获取元素
map.get("key");
// 删除元素
map.remove("key");
3、Guava 缓存
Guava是Google提供的一套JAVA的工具包,而Guava Cache
则是该工具包中提供的一套完善的JVM级别的高并发缓存框架。其实现机制类似ConcurrentHashMap,但是进行了众多的封装与能力扩展。作为JVM级别的本地缓存框架,Guava Cache
具备缓存框架该有的众多基础特性。
3.1特性
- 线程安全(和 ConcurrentHashMap 一样)
- 支持缓存记录的过期设定( 和 redis 缓存一样)
- 支持缓存容量限制与不同淘汰策略( FIFO 算法、LRU 算法)
3.2应用场景
初次接触到 Guava 感觉 redis 分布式缓存很像,具有同样的功能。但是区别在于,redis 分布式缓存是部署在应用之外的,存储大小受限于机器内存大小,在于应用通信存在网络消耗。Guava 是 JVM 本地缓存,与应用交互不存在网络消耗,访问效率快、但是存储大小受限于 JVM 堆内存大小。
应用场景:
- 存储内容小
- 命中率高
- 访问速度快
- 能够容忍数据不一致。(尝试使用定时任务进行同步或利用 cancel 订阅 binLog 进行数据同步)
注意:上面的场景并不是仅仅局限于 Guava 本地缓存,而是整个的本地缓存体系。
3.3 整合 Guava
3.3.1 导入 guava 依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
3.3.2 guava cache 创建
Cache<String, String> localCache = CacheBuilder.newBuilder()
// 内部哈希表的最小容量,也就是 cache 的初始容量
.initialCapacity(5)
// cache 的最大缓存数
.maximumSize(10)
// 并发等级,也可以定义为同时操作缓存的线程数,默认 4
.concurrencyLevel(3)
// 设置过期时间
.expireAfterWrite(10, TimeUnit.SECONDS)
.build();
3.3.3 获取对象
从缓存中获取数据调用的方法为 get (K key, Callable<? extends V> loader)
方法,此方法的含义是根据键 key 获取数据,若 key 不存在,则通过执行指定的 Callable 方法来构造缓存,示例代码如下所示:
localCache.get("key", new Callable<ThreadPoolConfigEntity>() {
@Override
public Object call() throws Exception {
System.out.println("未获取到 key 数据,可以进行回调");
// 进行数据写入,如查询 MySQL、 Redis
return null;
}
})
3.3.4 删除数据
3.3.4.1 被动删除
- 基于数据大小删除:LRU+FIFO
- 基于过期时间删除:在指定时间内没有被访问
- 基于引用删除:通过 weakKeys 和 weakValues 方法指定 Cache 只保存对缓存记录 key 和 value 的弱引用。这样当没有其他强引用指向 key 和 value 时,key 和 value 对象就会被垃圾回收器回收
3.3.4.2 主动删除
//删除指定的key对应数据
cache.invalidate("s");
//将一批对应的数据删除
cache.invalidateAll(Arrays.asList("st","r","ing"));
//全部删除
cache.invalidateAll();
3.3.5 源码分析 guava
浅析本地缓存技术 - Guava Cache | 京东物流技术团队 - 知乎 (zhihu.com)
3.3.6 Guava 其他应用
3.3.6.1 布隆过滤器
布隆过滤器 ; 一段 bitMap + 多个 Hash 函数,在添加元素的时候,会对元素进行相应的 hash 运算,将相应的 hash 槽,设置为 1,访问的时候也是进行 hash 运算,计算出相应的 hash 槽,当所有的 hash 槽都为 1 时,表示该数据才存在。
特点:
- 存在一定误差
- 查询结果不存在的情况,一定不存在
- 不可以进行元素的删除,只能整体删除
3.3.6.2 guava 实现布隆过滤器
public class BloomFilterTest {
public static void main(String[] args) {
long star = System.currentTimeMillis();
BloomFilter<Integer> filter = BloomFilter.create(
Funnels.integerFunnel(),
//预计存放多少数据
10000000,
//可以接受的误报率
0.01);
for (int i = 0; i < 10000000; i++) {
filter.put(i);
}
Assert.isTrue(filter.mightContain(1),"不存在");
Assert.isTrue(filter.mightContain(2),"不存在");
Assert.isTrue(filter.mightContain(3),"不存在");
Assert.isTrue(filter.mightContain(10000000),"不存在");
long end = System.currentTimeMillis();
System.out.println("执行时间:" + (end - star));
}
}
这里Guava引入了一个叫做Funnel
的类,Funnel类定义了如何把一个具体的对象类型分解为原生字段值,从而将值分解为Byte以供后面BloomFilter进行hash运算。通过使用这个类,我们可以自己定义一个属于自己类的Funnel。如下代码
enum PersonFunnel implements Funnel<Person> {
INSTANCE;
@Override
public void funnel(Object person, PrimitiveSink into) {
into.putString(person.getFirstName(), Charset.defaultCharset())
.putString(person.getLastName(), Charset.defaultCharset());
}
}
此外,Guava预定义了一些原生类型的Funnel,如String、Long、Integer
3.3.6.3 Guava 时间窗口限流
RateLimiter limiter = RateLimiter.create(5.0); // 创建一个每秒放入5个令牌的RateLimiter
for (int i = 0; i < 10; i++) {
// 请求一个令牌
limiter.acquire();
System.out.println("处理请求: " + i);
}
4、Caffeine ——本地缓存之王
推荐地址: java - 性能利器Caffeine缓存全面指南 - 宋小黑 - SegmentFault 思否、我的技术与生活——本地缓存之王-Caffeine | Hexo (iyaovo.github.io)
标签:map,存储,缓存,Java,本地,删除,3.3,key,Guava From: https://www.cnblogs.com/wzl66/p/18264121