1. 程式人生 > >2. java快取-程序內快取

2. java快取-程序內快取

#程序內快取
程序內快取的概述就不說了,可以檢視:java快取概述
本篇文章主要說說常見的程序內快取,這裡分為兩類:自定義程序內快取、輕量級程序內快取框架。

##自定義程序快取

利用java的基本資料結構List、Map、Set等,自定義一個快取對應。
自定義程序快取示例:

import java.util.HashMap;

public class MyCache {
	//測試程式碼
    public static void main(String[] args) {

        CustomCache cache = CustomCache.instance();

        cache.put("2313", 13213);
        cache.put("jkf1", 132);

        System.out.println(cache.get("jkf"));

        System.out.println(cache.get("jkf1"));
    }

}
//自定義快取示例,單利必須是要保證的
class CustomCache<V> {
    private static CustomCache instance = new CustomCache();


    private HashMap<String, V> cache = new HashMap<>();

	private CustomCache(){}
     
    public static CustomCache instance() {

        return instance;

    }


    public void put(String key, V t) {

        this.cache.put(key, t);

    }


    public V get(String key) {

        return cache.get(key);

    }

}

上述自定義示例,就是簡單的包裝一個HahMap,對頻繁訪問的資料進行快取。自定義快取存在如下的缺點:

  1. 多執行緒併發訪問。
  2. 儲存資料過多,存在記憶體溢位,即沒有快取淘汰策略。
  3. 一些快取的統計資料無法提供。
    當然上述缺陷,我們都可以在上述程式碼上,進行精細化開發,解決上述的缺點,但是這樣的開發量比較大,與初衷背離:我只是想簡簡單單的使用一下,不想再開發一個專業的快取。

輕量級程序內快取框架

主要是說google的guava cache,這個輕量級的執行緒內快取。

###使用guava cache
a. 引入方便,只需要引用google開源java類庫即可,guava cache只是其中的一個包。

b. 使用簡潔,Guava cache對泛型具有良好的支援,支援多種型別的快取。例如:
String=Object,Integer=Object。

c. 不需要配置檔案,直接通過程式碼配置快取的各種引數,例如:併發執行緒量(concurrencyLevel,寫的執行緒數)、容器初始容量(initialCapacity)、快取移除通知(removalListener)、快取不命中的載入資料方法(CacheLoader的load)、快取失效策略(expireAfterWrite、maximumSize(LRU)),當然上述方法中“快取不命中的載入資料方法”只有特殊的快取例項支援。

d. 最特殊的:guava cache 是單執行緒,guava cache不會建立執行緒來維護快取。

###使用示例
guava cache的快取例項型別有多種,這裡只是簡單的介紹“可載入快取(LoadingCache)”的使用,其他型別的快取例項,請看下一篇專門的guava cache架構分析。

通過時間失效進行快取淘汰

public static void main(String[] args) {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
		        //快取失效策略:寫後30分鐘
                .expireAfterWrite(30L, TimeUnit.MINUTES)
                //快取未命中的策略:通過load方法查詢獲取資料
                .build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) throws Exception {
                        return new Random().nextInt(10);
                    }

                });

    } 

更完善的示例

public static void main(String[] args) {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                .concurrencyLevel(8)
                .initialCapacity(10)
                .maximumSize(100)
                .recordStats()
                .removalListener(new RemovalListener<String, Integer>() {
                    @Override
                    public void onRemoval(RemovalNotification<String, Integer> notification) {
                    }
                }).build(new CacheLoader<String, Integer>() {
                    @Override
                    public Integer load(String key) throws Exception {
                        return new Random().nextInt(10);
                    }
                });
    }

###什麼時候使用guava cache
使用場景

  1. 你願意消耗一些記憶體空間來提升速度。
  2. 你預料到某些鍵會被查詢一次以上。
  3. 快取中存放的資料總量不會超出記憶體容量。(Guava Cache是單個應用執行時的本地快取。它不能把資料存放到檔案或外部伺服器。如果這不符合你的需求,請嘗試Memcached這類工具)
  4. guava cache可以快取 少量的 頻繁查詢 的資料。
  5. guava cache的快取失效,並不是立刻失效,而是延遲失效,原因:guava cache並不會啟動執行緒,而是利用利用當前執行緒,進行快取失效處理,例如會在寫操作後呼叫快取清理,如果寫操作太少會在讀操作進行快取清理。當然如果對快取清理要求嚴格,可以自己建立維護執行緒,進行快取清理工作,例如:定時任務、其他執行緒
  6. 因為使用的是快取,所以需要考慮快取一致性對需求的要求。
  7. 因為是程序內快取,guava cache沒有提供快取的持久化。
  8. Guava cache可以根據不同的業務設定不同的cache,可以做到同一業務的同一處理,但是又會引發快取的離散,這個需要衡量。

guava cache的框架分析,見下一篇