项目github地址:bitcarmanlee easy-algorithm-interview-and-practice
欢迎大家star,留言,一起学习进步
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方法去从外部加载数据进来。