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垃圾回收。