<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
import com.google.common.cache.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class _1_CacheLoaderBasicTest {
public static void main(String[] args) throws Exception{
// test1();
// test2();
// test3();
// test4();
// test5();
// test6();
// test7();
// test8();
// test9();
test10();
}
//測試緩存刷新睛榄,
//注:guava的緩存刷新不是后臺(tái)根據(jù)緩存時(shí)間去刪除緩存的 而是在程序訪問的時(shí)候判斷 如果緩存到期了 那么就會(huì)去從from方法里重新構(gòu)建數(shù)據(jù)然后放到緩存里面來
private static void test9() throws Exception{
AtomicInteger counter = new AtomicInteger(0);
CacheLoader<String, String> cacheLoader = CacheLoader.from(k -> {
counter.incrementAndGet();
return k;
});
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.refreshAfterWrite(2, TimeUnit.SECONDS)
.build(cacheLoader);
String result1 = cache.getUnchecked("Alex");
TimeUnit.SECONDS.sleep(3);
String result2 = cache.getUnchecked("Alex");
System.out.println(counter.get());//2 双揪,中間數(shù)據(jù)被過期了 會(huì)重新去加載一次 所以這里是2
}
//由createCacheLoader()可以知道窗骑,不能返回null值給cache门坷,否則會(huì)報(bào)錯(cuò)
//所以我們需要Optional的方式來避免這個(gè)問題
private static void test6() {
CacheLoader<String, Optional<Employee>> loader = new CacheLoader<String, Optional<Employee>>() {
@Override
public Optional<Employee> load(String key) {
if (key.equals("null"))
return Optional.ofNullable(null);
else
return Optional.of(new Employee(key, key, key));
}
};
LoadingCache<String, Optional<Employee>> cache = CacheBuilder.newBuilder() .build(loader);
System.out.println(cache.getUnchecked("Alex").get());
Employee abc = cache.getUnchecked("null").orElse(new Employee("default", "default", "default"));
System.out.println(abc);//null 拿出來的數(shù)據(jù)是空,這里返回默認(rèn)的數(shù)據(jù)
//也可以通過下面這種方式來做處理
Optional<Employee> def = cache.getUnchecked("null");
if(!def.isPresent()){
System.out.println("拿到的數(shù)據(jù)為空");
}
}
/**測試軟引用在緩存中被過期
* 需要設(shè)置堆內(nèi)存信息
* -Xms64M -Xmx64M -XX:+PrintGCDetails
* @throws Exception
*/
private static void test5() throws Exception{
LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
.expireAfterWrite(200, TimeUnit.SECONDS)
.softValues()//只能對(duì)values 做軟引用處理
.build(createCacheLoader2());
int i = 0;
//Employee 內(nèi)部會(huì)有一個(gè)1M的數(shù)組伍掀,所以一個(gè)Employee對(duì)象 至少占1M空間
//由于軟引用在內(nèi)存快不足的時(shí)候會(huì)被回收 所以這里運(yùn)行結(jié)果 可以創(chuàng)建很多對(duì)象 不止64個(gè)
//todo 但是考慮到軟引用的性能影響 一般我們在在緩存中設(shè)置size來限制緩存所占內(nèi)存的總大小 而不是使用軟引用來
for (; ; ) {
String key = "Alex" + i;
cache.put(key, new Employee(key, key, key));
// cache.get(key);
// System.gc();
System.out.println("The Employee [" + (i++) + "] is store into cache.");
TimeUnit.MILLISECONDS.sleep(200);
}
}
//測試弱引用(weakReference) 在緩存中被過期
public static void test4() throws InterruptedException {
LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.SECONDS)
.weakValues()
.weakKeys()
.build(createCacheLoader2());
cache.getUnchecked("Alex");
cache.getUnchecked("Guava");
//active method
//Thread Active design pattern
System.gc();
TimeUnit.MILLISECONDS.sleep(100);
//weak:弱引用 每次gc的時(shí)候 都會(huì)將其回收必指, 所以這里從緩存中拿不到數(shù)據(jù)了
System.out.println(cache.getIfPresent("Alex"));
}
//測試Write的expire模式下的緩存存活情況
private static void test3() throws Exception {
LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
//expireAfterWrite,write:包括寫(write)和更新(update) 不包括讀(read)
.expireAfterWrite(2, TimeUnit.SECONDS)
.build(createCacheLoader2());
cache.getUnchecked("Guava");
TimeUnit.SECONDS.sleep(1);
Employee guava = cache.getIfPresent("Guava");
System.out.println(guava);//1s后guava 此時(shí)存活
TimeUnit.MILLISECONDS.sleep(900);
guava = cache.getIfPresent("Guava");
System.out.println(guava);//1.90s guava 此時(shí)依然存活
TimeUnit.SECONDS.sleep(1);
guava = cache.getIfPresent("Guava");
System.out.println(guava);//2.99s guava 此時(shí)不存活
}
//測試Access的expire模式下的緩存存活情況
private static void test2() throws Exception{
LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
//expireAfterAccess,access:包括讀(read),寫(write),改(update),都會(huì)續(xù)長緩存的存活期
.expireAfterAccess(2, TimeUnit.SECONDS)
.build(createCacheLoader2());
cache.getUnchecked("Alex");
TimeUnit.SECONDS.sleep(3);
Employee alex = cache.getIfPresent("Alex");//睡眠了 3s 此時(shí)緩存獲取不到數(shù)據(jù)
System.out.println(alex);
cache.getUnchecked("Guava");
TimeUnit.SECONDS.sleep(1);
Employee employee = cache.getIfPresent("Guava");//此時(shí)Guava存在
System.out.println(employee);
TimeUnit.SECONDS.sleep(1);
employee = cache.getIfPresent("Guava");//再次獲取Guava 依然存在
System.out.println(employee);
TimeUnit.SECONDS.sleep(1);
employee = cache.getIfPresent("Guava");//再次獲取Guava 依然存在
System.out.println(employee);
}
//測試基礎(chǔ)用法
public static void test1() throws ExecutionException, InterruptedException {
//創(chuàng)建一個(gè)緩存容器對(duì)象 其最大容量是3,容器內(nèi)元素存放30ms就過期
LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
.maximumSize(3)
.expireAfterAccess(30, TimeUnit.MILLISECONDS)
.build(createCacheLoader());
//#################################### 測試30ms時(shí)間過期 ####################################
// System.out.println(cache.get("Alex").getName());
// TimeUnit.MILLISECONDS.sleep(31);
// System.out.println(cache.get("Alex").getName());
//測試到達(dá)容量之后 LRU過期
//#################################### 測試到達(dá)size之后被LRU過期(過期策略:size) ####################################
// System.out.println(cache.get("Alex").getName());
// System.out.println(cache.get("allen").getName());
// System.out.println(cache.get("tom").getName());
// System.out.println(cache.get("amy").getName());//此時(shí)Alex被過期調(diào)入录,再拿Alex的話會(huì)從DB中拿
// System.out.println(cache.size());
// System.out.println(cache.get("Alex").getName());
// System.out.println();
//#################################### 測試到達(dá)weight之后被LRU過期(過期策略:自定義) ####################################
//設(shè)置一個(gè)稱重器 稱重的方式是:對(duì)象的重量weight = name的長度 + empId的長度 + Dept的長度
Weigher<String, Employee> weigher = (key, employee) ->
employee.getName().length() + employee.getEmpID().length() + employee.getDept().length();
//設(shè)置緩存重量限制為45
LoadingCache<String, Employee> cache2 = CacheBuilder.newBuilder()
.maximumWeight(45)
.concurrencyLevel(1)
.weigher(weigher)
.build(createCacheLoader());
cache2.get("Gavin");//緩存重量:15
cache2.get("Kevin");//緩存重量:30
cache2.get("Allen");//緩存重量:45
cache2.get("Jason");//重量已經(jīng)達(dá)到45,此時(shí)Gavin被過期
Employee employee = cache2.getIfPresent("Gavin");//此時(shí)再去緩存中拿Gavin 是拿不到的
System.out.println(employee);
//#################################### LoadingCache 的一些api ####################################
// 從LoadingCache中拿數(shù)據(jù)佳镜,如果拿不到僚稿,會(huì)去從DB中拿,
// cache.get("aa");
// 與get()方法的區(qū)別是這里不需要顯式捕獲異常
// cache.getUnchecked("aa");
// 從緩存中拿key對(duì)應(yīng)的數(shù)據(jù)蟀伸,如果緩存中沒有 就返回null蚀同,不會(huì)去從DB中拿
// cache.getIfPresent("aa");
}
// //測試緩存刷新
// public void testCacheRefresh() throws InterruptedException {
// AtomicInteger counter = new AtomicInteger(0);
// CacheLoader<String, Long> cacheLoader = CacheLoader .from(k -> {
// counter.incrementAndGet();
// return System.currentTimeMillis();
// });
//
// LoadingCache<String, Long> cache = CacheBuilder.newBuilder()
//// .refreshAfterWrite(2, TimeUnit.SECONDS)
// .build(cacheLoader);
//
// Long result1 = cache.getUnchecked("Alex");
// TimeUnit.SECONDS.sleep(3);
// Long result2 = cache.getUnchecked("Alex");
// assertThat(counter.get(), equalTo(1));
//// assertThat(result1.longValue() != result2.longValue(), equalTo(true));
// }
//測試緩存預(yù)加載:可以提前構(gòu)造好一個(gè)hashMap 然后將其放入到緩存中
//注意:這種緩存構(gòu)建方法不會(huì)經(jīng)過緩存加工,會(huì)直接作為緩存數(shù)據(jù)存進(jìn)去
public static void test7() {
CacheLoader<String, String> loader = CacheLoader.from(String::toUpperCase);
LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(loader);
Map<String, String> preData = new HashMap<String, String>() { {
put("alex", "ALEX");
put("hello", "hello");
}
};
cache.putAll(preData);
System.out.println(cache.size());//2
System.out.println(cache.getUnchecked("alex"));
System.out.println(cache.getUnchecked("hello"));
}
//測試淘汰通知:緩存被淘汰時(shí) 應(yīng)用程序可以拿到被淘汰的信息
public static void test8(){
CacheLoader<String, String> loader = CacheLoader.from(String::toUpperCase);
RemovalListener<String, String> listener = notification ->
{
if (notification.wasEvicted())
{
RemovalCause cause = notification.getCause();
System.out.println(notification.getKey().equals("Alex"));//true 被淘汰的元素是Alex
System.out.println(cause.equals(RemovalCause.SIZE));//true 被淘汰的原因是容量不足
}
};
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
//
.maximumSize(3)
//
.removalListener(listener)
//
.build(loader);
cache.getUnchecked("Alex");
cache.getUnchecked("Eachur");
cache.getUnchecked("Jack");
cache.getUnchecked("Jenny");
}
//測試緩存命中情況
private static void test10() {
CacheLoader<String, String> loader = CacheLoader.from(String::toUpperCase);
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(5)
.recordStats()
.build(loader);
// 緩存創(chuàng)建也可以按照表達(dá)式來創(chuàng)建 如下:
// String spec = "maximumSize=5,recordStats";
// CacheBuilderSpec builderSpec = CacheBuilderSpec.parse(spec);
// CacheLoader<String, String> loader = CacheLoader.from(String::toUpperCase);
// LoadingCache<String, String> cache = CacheBuilder.from(builderSpec).build(loader);
cache.getUnchecked("alex");
CacheStats stats = cache.stats();
System.out.println(stats.hashCode());
System.out.println(stats.hitCount());//0
System.out.println(stats.missCount());//1 第一次沒在緩存中查到 所以也記錄到了命中
cache.getUnchecked("alex");
stats = cache.stats();
System.out.println(stats.hashCode());//狀態(tài)變化了 stat的hashcode會(huì)變化
System.out.println(stats.hitCount());//1
System.out.println(stats.missCount());//1
System.out.println(stats.missRate());//0.5
System.out.println(stats.hitRate());//0.5
}
private static CacheLoader<String, Employee> createCacheLoader() {
return new CacheLoader<String, Employee>() {
@Override
public Employee load(String key) throws Exception {
// 這里要注意:load的時(shí)候不能返回空啊掏, 那返回空了怎么辦蠢络?很有可能命中不了
// if (key.equals("aa")) return null;
System.out.println("從數(shù)據(jù)庫中拿數(shù)據(jù): " + key);
return new Employee(key, key, key);
}
};
}
private static CacheLoader<String, Employee> createCacheLoader2() {
//也可以使用from()來構(gòu)建緩存
return CacheLoader.from(key -> new Employee(key, key, key));
}
}