容量回收
、定时回收
和基于引用回收
。定时回收有两种:按照写入时间,最早写入的最先回收;按照访问时间,最早访问的最早回收。超时机制不是精确的。
public static void main(String[] args) throws ExecutionException, InterruptedException{
//缓存接口这里是LoadingCache,LoadingCache在缓存项不存在时可以自动加载缓存
LoadingCache<Integer,Student> studentCache
//CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
= CacheBuilder.newBuilder()
//设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(8)
//设置写缓存后8秒钟过期
.expireAfterWrite(8, TimeUnit.SECONDS)
//设置缓存容器的初始容量为10
.initialCapacity(10)
//设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
.maximumSize(100)
//设置要统计缓存的命中率
.recordStats()
//设置缓存的移除通知
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(
new CacheLoader<Integer, Student>() {
@Override
public Student load(Integer key) throws Exception {
System.out.println("load student " + key);
Student student = new Student();
student.setId(key);
student.setName("name " + key);
return student;
}
}
);
for (int i=0;i<20;i++) {
//从缓存中得到数据,由于我们没有设置过缓存,所以需要通过CacheLoader加载缓存数据
Student student = studentCache.get(1);
System.out.println(student);
//休眠1秒
TimeUnit.SECONDS.sleep(1);
}
System.out.println("cache stats:");
//最后打印缓存的命中率等 情况
System.out.println(studentCache.stats().toString());
}
输出结果
cache stats:
CacheStats{hitCount=17, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=1348802, evictionCount=2}
原因:看看到在20此循环中命中次数是17次,未命中3次,这是因为我们设定缓存的过期时间是写入后的8秒,所以20秒内会失效两次,另外第一次获取时缓存中也是没有值的,所以才会未命中3次,其他则命中。
V getIfPresent(Object key)
获取缓存中key对应的value,如果缓存没命中,返回null。V get(K key) throws ExecutionException
获取key对应的value,若缓存中没有,则调用LocalCache的load方法,从数据源中加载,并缓存。void put(K key, V value)
如果缓存有值,覆盖,否则,新增void putAll(Map m);
循环调用单个的方法void invalidate(Object key);
删除缓存void invalidateAll();
清楚所有的缓存,相当远map的clear操作。long size();
获取缓存中元素的大概个数。为什么是大概呢?元素失效之时,并不会实时的更新size,所以这里的size可能会包含失效元素。CacheStats stats();
缓存的状态数据,包括(未)命中个数,加载成功/失败个数,总共加载时间,删除个数等。asMap()
方法获得缓存数据的ConcurrentMap快照cleanUp()
清空缓存refresh(Key)
刷新缓存,即重新取缓存数据,更新缓存ImmutableMap getAllPresent(Iterable keys)
一次获得多个键的缓存值import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Maps;
public class CacheUtil {
private static final Logger logger = LoggerFactory.getLogger(CacheUtil.class);
private static final RedisUtil redisUtil = RedisUtil.getInstance();
/**
* load方式缓存
*
* @param key
* @param value
*/
public static void putByLoad(String key, Object value) {
String valStr = JSON.toJSONString(value);
logger.info("[CacheUtil] putByLoad, key -> " + key + ", value -> " + valStr);
CacheUtil.getInstanceForLoad().put(key, valStr);
redisUtil.set(key, valStr);
}
/**
* load方式读取单个bean
*
* @param key
* @param t
* @return
* @throws Exception
*/
public static <T> T getObjectByLoad(String key, Class<T> t) {
if (key == null)
return null;
String object = null;
try {
object = CacheUtil.getInstanceForLoad().get(key);
logger.info("[CacheUtil] get from cache, key -> " + key + ", value -> " + object);
} catch (Exception e) {
logger.error("[CacheUtil] getByLoad error -> " + e);
}
if (object == null)
return null;
return (T) JSON.parseObject(object, t);
}
/**
* load方式读取list
*
* @param key
* @param t
* @return
*/
public static <T> List<T> getListByLoad(String key, Class<T> t) {
if (key == null)
return null;
String object = null;
try {
object = CacheUtil.getInstanceForLoad().get(key);
} catch (Exception e) {
logger.error("[CacheUtil] getByLoad error -> " + e);
}
if (object == null)
return null;
return JSON.parseArray(object, t);
}
/**
* load方式读取map
*
* @param key
* @param t
* @return
*/
public static <T> Map<String, T> getMapByLoad(String key, Class<T> t) {
if (key == null)
return null;
String object = null;
try {
object = CacheUtil.getInstanceForLoad().get(key);
logger.info("[CacheUtil] get from cache, key -> " + key + ", value -> " + object);
} catch (Exception e) {
logger.error("[CacheUtil] getByLoad error -> " + e);
}
if (object == null)
return null;
Map<String, T> map = JSONObject.parseObject(object, new TypeReference<Map<String, T>>() {
});
for (String k : map.keySet()) {
T o = JSON.parseObject(map.get(k).toString(), t);
map.put(k, o);
}
return map;
}
/**
* map方式缓存
*
* @param key
* @param value
*/
public static void putByMap(String key, Object value) {
CacheUtil.getInstanceForMap().put(key, JSON.toJSONString(value));
}
/**
* map方式读取
*
* @param key
* @param t
* @return
*/
public static <T> T getByMap(String key, Class<T> t) {
Object object = CacheUtil.getInstanceForMap().getIfPresent(key);
if (object == null)
return null;
return (T) JSON.parseObject((String) object, t);
}
public static LoadingCache<String, String> getInstanceForLoad() {
return SingleTonForLoad.cahceBuilder;
}
private static class SingleTonForLoad {
private static LoadingCache<String, String> cahceBuilder = CacheBuilder.newBuilder()
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
redisUtil.expire(key, 0);
String re = redisUtil.get(key);
logger.info("[CacheUtil] get from redis, key -> " + key + ", value -> " + re);
return re;
}
});
}
public static Cache<String, Object> getInstanceForMap() {
return SingleTonForMap.cache;
}
private static class SingleTonForMap {
private static Cache<String, Object> cache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES)
.build();
}
public static void main(String[] args) {
Map<String, CompetitionBean> map1 = Maps.newHashMap();
CompetitionBean bean1 = new CompetitionBean();
bean1.setCategory("足球");
map1.put("1", bean1);
CacheUtil.putByLoad("a", map1);
System.err.println(JSON.toJSONString(CacheUtil.getMapByLoad("a", CompetitionBean.class)));
Map<String, CompetitionBean> map2 = Maps.newHashMap();
CompetitionBean bean2 = new CompetitionBean();
bean2.setCategory("篮球");
map2.put("2", bean2);
CacheUtil.putByLoad("a", map2);
System.err.println(JSON.toJSONString(CacheUtil.getMapByLoad("a", CompetitionBean.class)));
}
}
参考博文,点击这里
在每次从cache中get(K)时,如果不存在会自动调用load方法原子的将值计算出来并加到缓存中。(调用load方法是同步的)
getAll(Iterable extendsK>)
方法用来执行批量查询注意:getAll()方法也是guava cache中的方法。默认情况下,对每个不在缓存中的键,getAll方法会单独调用CacheLoader.load来加载缓存项。如果批量的加载比多个单独加载更高效,可以重载CacheLoader.loadAll来利用这一点提示getAll(Iterable)的性能。看一个例子:
参考文章,点击这里
LoadingCache userCache = CacheBuilder.newBuilder()
.maximumSize(10000))//设置缓存上线
.expireAfterAccess(10, TimeUnit.MINUTES)//设置时间对象没有被读/写访问则对象从内存中删除
.expireAfterWrite(10, TimeUnit.MINUTES)//设置时间对象没有被写访问则对象从内存中删除
//移除监听器,缓存项被移除时会触发
.removalListener(new RemovalListener<String, UserProfile>() {
@Override
public void onRemoval(RemovalNotification<String, UserProfile> notification) {
//逻辑
}
}
})
.recordStats()
//CacheLoader类 实现自动加载
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) {
//从SQL或者NoSql 获取对象
}
});
Map<MetaCacheKey, CacheValue<T>> cacheRes = cache.getAll(keys);
for (Map.Entry<MetaCacheKey, CacheValue<T>> cacheMapEntry: cacheRes.entrySet()) {
CacheValue<T> cacheValue = cacheMapEntry.getValue();
if (cacheValue.getV() != null) {
res.put(cacheMapEntry.getKey().getOriginHashId(), cacheValue);
checkExpire(cacheMapEntry.getKey(), cacheValue);
} else {
cache.invalidate(cacheMapEntry.getKey());
}
}
refreshAfterWrite的使用参考文章
参考文章2
主要参考文章,点击这里
package net.aty.guava;
import com.google.common.base.Stopwatch;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class Main {
// 模拟一个需要耗时2s的数据库查询任务
private static Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("begin to mock query db...");
Thread.sleep(2000);
System.out.println("success to mock query db...");
return UUID.randomUUID().toString();
}
};
// 1s后刷新缓存
private static LoadingCache<String, String> cache = CacheBuilder.newBuilder().refreshAfterWrite(1, TimeUnit.SECONDS)
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
return callable.call();
}
});
private static CountDownLatch latch = new CountDownLatch(1);
public static void main(String[] args) throws Exception {
// 手动添加一条缓存数据,睡眠1.5s让其过期
cache.put("name", "aty");
Thread.sleep(1500);
for (int i = 0; i < 8; i++) {
startThread(i);
}
// 让线程运行
latch.countDown();
}
private static void startThread(int id) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "...begin");
latch.await();
Stopwatch watch = Stopwatch.createStarted();
System.out.println(Thread.currentThread().getName() + "...value..." + cache.get("name"));
watch.stop();
System.out.println(Thread.currentThread().getName() + "...finish,cost time=" + watch.elapsed(TimeUnit.SECONDS));
} catch (Exception e) {
e.printStackTrace();
}
}
});
t.setName("Thread-" + id);
t.start();
}
}