天天看點

使用WeakHashMap建立具有過期時間的本地緩存

java中為了讓程式員能參與到垃圾回收機制中(不是說java的GC是全自動的嗎?這要看從哪個方面講了,其實如果開發人員能熟練掌握java的gc原理,對提升系統的穩定性有極大幫助),設計了4種引用級别:分别是強引用(new 出來的)、軟引用、弱引用和虛引用。

本文不打算介紹這幾種引用的用法。還不了解它們的讀者可以自行百度(國内的幾個搜尋引擎,還是百度 最适合程式員)。

不過如果能使用谷歌的還是要使用谷歌,因為各個級别都有大坑,使用不當可能完全發揮不了期望的效果。但是國内很少有文章講其中的注意事項,可能是使用的場景太少了。而國外(比如dzone)的部落格就不少。

弱引用的對象在每次垃圾回收的時候都會被處理掉。WeakHashMap就是利用了這個特性來實作的本地緩存。

WeakHashMap的具體用法也請百度。這裡假設你已經在用WeakHashMap了

WeakHashMap實作的緩存雖然好用,但是不具備定時過期的特性。如果沒有等到垃圾回收(或者我不去看監控根本不知道是否進行過垃圾回收),但是想更新緩存之值,就無能為力了!

是以這裡我們個WeakHashMap增加一個過期時間。

有兩種方式實作:

  • 通過繼承WeakHashMap實作
  • 将WeakHashMap作為一個屬性來實作

我們這裡使用第一種,思路是把本來要放進WeakHashMap的東西附加一個目前時間。是以在寫出繼承WeakHashMap 的類之前先寫一個附加時間點的封裝類:

public class TimedLocalCachedDataDTO<V> {
    private final LocalDateTime time;
    private final V data;

    public TimedLocalCachedDataDTO(V data) {
        this.time = LocalDateTime.now();
        this.data = data;
    }
    // getters
}           

這樣,我們的WeakHashMap要放的對象都是

TimedLocalCachedDataDTO

的執行個體:

public class TimedLocalCache<K, V> extends WeakHashMap<K, TimedLocalCachedDataDTO<V>> {           

這個類裡,我們給它指定一個過期時間:

public class TimedLocalCache<K, V> extends WeakHashMap<K, TimedLocalCachedDataDTO<V>> {
    private final long timeout;

    public TimedLocalCacheDTO(long timeout) {
        this.timeout = timeout;
    }

    /**
     * 預設5分鐘過期
     */
    public TimedLocalCacheDTO() {
        timeout = TimeUnit.MINUTES.toSeconds(5);
    }

    public long getTimeout() {
        return timeout;
    }           

然後在擷取值的時候,如果根據key擷取到值了,并不直接傳回;而是先拿到放入的時間點加上過期時間與目前時間比較。下面分别是寫入和讀取的方法:

public TimedLocalCachedDataDTO<V> putCache(K key, V value) {
        TimedLocalCachedDataDTO<V> dataDTO = new TimedLocalCachedDataDTO<>(value);
        return super.put(key, dataDTO);
    }           
public V getCache(K key) {
        TimedLocalCachedDataDTO<V> dataDTO = super.get(key);
        if (dataDTO == null) {
            return null;
        }
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime time = dataDTO.getTime();
        long seconds = Duration.between(time, now).getSeconds();
        if (seconds > getTimeout()) {
            remove(key);
            return null;
        }
        return dataDTO.getData();
    }           

這樣,我們的WeakHashMap就可以指定一個過期時間了,而不用等待JVM垃圾回收。