1.Guava中的cache
cache在任何系統中都是一種被廣泛使用的數據中間件。對於小規模的緩存數據,Guava中的cache會很實用,使用得也很多。下面我們就針對Guava中的cache做個簡單分析。
/**
* A semi-persistent mapping from keys to values. Cache entries are manually added using
* {@link #get(Object, Callable)} or {@link #put(Object, Object)}, and are stored in the cache until
* either evicted or manually invalidated.
*
* <p>Implementations of this interface are expected to be thread-safe, and can be safely accessed
* by multiple concurrent threads.
*
* @author Charles Fry
* @since 10.0
*/
@GwtCompatible
public interface Cache<K, V>
從jdk源碼中的註釋裏,我們可以得到如下信息:
1.Cache接口是一種半持久化的KV結構。
2.Cache可以使用get(Object, Callable)方法或者put方法手動添加元素對。
3.這些KV會一直有效,直到被驅逐或者手動設置爲無效。
4.該接口事線程安全的,可以被多個線程併發訪問。
2.LoadingCache
Guava Cache與ConcurrentMap比較相似,最大的不同在於ConcurrentMap會一直保存添加的元素直到被手動移除。而Guava Cache爲了限制內存的使用會自動回收元素,而且在很多場景下,需要自動加載緩存,這也是對比ConcurrentMap的優勢。
LoadingCache繼承了Cache接口。LoadingCache讀取一個指定key的數據時,如果key不存在,LoadingCache會執行加載數據到緩存。
3.LoadingCache例子
import com.google.common.cache.CacheLoader;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.List;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyCacheLoader extends CacheLoader<String, List<Integer>> {
private final ListeningExecutorService executorService =
MoreExecutors.listeningDecorator(new ThreadPoolExecutor(16,
100,
60,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
new NamedThreadFactory("xxx")));
protected ListeningExecutorService executorService() {
return executorService;
}
@Override
public List<Integer> load(String key) {
long startTime = System.currentTimeMillis();
List<Integer> result = (一般爲數據庫操作,獲取數據)
return result;
}
@Override
public ListenableFuture<List<Integer>> reload(String key, List<Integer> oldValue) {
return executorService().submit(() -> load(key));
}
}
public class MyLoadingCache {
private static final int CACHE_TTL = 60 * 6;
private static final int CACHE_MAX_SIZE = 10000;
private static LoadingCache<String, List<Integer>> myLoadingCache = CacheBuilder.newBuilder()
.refreshAfterWrite(CACHE_TTL, TimeUnit.MINUTES)
.maximumSize(CACHE_MAX_SIZE)
.build(new ReaderOnlineBadContentCacheLoader());
}
LoadingCache類型的對象是通過CacheBuilder進行構建的,從構建過程中很明顯可以看出來是使用的build模式,調用的每個方法返回的都是CacheBuilder本身,直到build方法被調用,才返回LoadingCache對象。
build方法需要傳入一個CacheLoader對象,CacheLoader是一個抽象類,需要重寫load方法。
@GwtCompatible(emulated = true)
public abstract class CacheLoader<K, V> {
...
public abstract V load(K key) throws Exception;
}
如果我們調用LoadingCache中的get方法,緩存不存在相對應的key的數據,那麼CacheLoader會自動調用load方法去從外部加載數據進來。