java實現帶過期時間的緩存


緩存在我們開發中十分常見,許多框架提供了緩存機制,如果我們自己需要實現一個緩存,該怎么實現呢?

現在有個需求:我們有個配置信息,只有一份,這個信息我們存儲到redis中:鍵的名稱為config,值為json字符串,比如:

{
    "time":10,
    "type":1,
    "threshold":1000
}

       假如我們對這個config里面的內容使用十分頻繁,但是這個配置信息更改卻不怎么頻繁,並且這個更改不一定要實時生效,那么我們可以不用每次使用這個配置信息的時候都去查詢redis,因為對redis的性能會有所影響。我們考慮到在應用層使用緩存,將配置信息在應用層緩存起來,每隔一分鍾自動清空一下緩存,清空緩存之后,下次請求就會訪問redis,獲取最新的配置信息。當然這之間配置信息可能已經更改,更改之后到應用最近一次從redis獲取數據有一個時間間距,這段時間所使用的配置信息可能不是最新的,當然我們可以忍受這一點。

注意本次博文我們想緩存一個對象,而不是很多數據。

一:簡單實現


1,首先我們假如已經有了一個查詢配置信息的方法:

 public MyConfig getConfig(){ return JSONObject.parseObject(stringRedisTemplate.opsForValue().get("config"), MyConfig.class); }

上面代碼只是演示,部分代碼在博文中沒有貼出。

  • MyConfig類內容和上面的json字符串對應。
  • get("config")中的config是redis中的鍵。

當程序調用上面的getConfig方法,每次都會從redis獲取數據,現在我們對代碼進行改造。

2,新建一個緩存類ConfigCache:

public final class ConfigCache {

    private static MyConfig myConfig = null;

    private ConfigCache() {
    }

    static {
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleAtFixedRate(() -> {
            myConfig = null;
        }, 0, 60, TimeUnit.SECONDS);
    }

    public static MyConfig get() {
        return myConfig;
    }

    public static void put(MyConfig newConfig) {
        myConfig = newConfig;
    }
}

我們定義了一個緩存類,持有一個要緩存的對象MyConfig,提供獲取和設置方法。並且在每隔60秒清空一次該MyConfig對象,這就實現了緩存對象過期時間的功能。

3,重新編寫查詢配置信息的代碼:

    public MyConfig getConfig(){
        MyConfig myConfig = ConfigCache.get();
        if(myConfig == null){
            myConfig = JSONObject.parseObject(stringRedisTemplate.opsForValue().get("config"), MyConfig.class);
            ConfigCache.put(myConfig);
        }
        return myConfig;
    }

先查詢本地緩存,如果本地緩存為空,則從redis查詢,並且保存至本地緩存;如果本地緩存不為空,則直接使用本地緩存。

到此上面的簡單需求我們就實現了。

二:借用java8 computeIfAbsent 方法優化代碼


image

上面圖片來自《Java 8函數式編程》一書。

很顯然1.3節的內容和圖片上面的5-31節代碼類似,java8提供了computeIfAbsent 方法簡化開發。我們可以提供自己的computeIfAbsent 方法,然后優化代碼。

1,改造緩存類ConfigCache

public final class ConfigCache {

    private static MyConfig myConfig = null;

    private ConfigCache() {
    }

    static {
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleAtFixedRate(() -> {
            myConfig = null;
        }, 0, 60, TimeUnit.SECONDS);
    }

    public static MyConfig computeIfAbsent(Supplier<MyConfig> supplier) {
        if (myConfig == null) {
            myConfig = supplier.get();
        }
        return myConfig;
    }
}

我們刪除了get和put方法,新增了computeIfAbsent 方法,該方法需要一個Supplier,它提供一個MyConfig對象。

computeIfAbsent 代碼主要邏輯是如果myConifg不為空,則返回該對象,否則通過Supplier構造一個對象給ConfigCache類的靜態屬性賦值,並返回該對象。

2,重新編寫查詢配置信息的實現的代碼:

    public MyConfig getConfig(){
        return ConfigCache.computeIfAbsent(JSONObject.parseObject(stringRedisTemplate.opsForValue().get("config"), MyConfig.class));
    }

此時查詢配置信息的方法就很簡單了,和上面圖片改造的類似,代碼看起來也簡潔許多。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM