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 時間視窗限流
阿里面試:說說自適應限流?_王磊_InfoQ寫作社群
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)