<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>
package com.example.springboottest.guava_cache;
import com.google.common.base.MoreObjects;
/***************************************
@author:Alex Wang
@Date:2017/11/18
QQ: 532500648
-
QQ群:463962286
***************************************/
public class Employee
{
private final String name;
private final String dept;
private final String empID;
private final byte[] data = new byte[1024 * 1024];public Employee(String name, String dept, String empID)
{
this.name = name;
this.dept = dept;
this.empID = empID;
}public String getName()
{
return name;
}public String getDept()
{
return dept;
}public String getEmpID()
{
return empID;
}@Override
public String toString()
{
return MoreObjects.toStringHelper(this)
.add("Name", this.getName()).add("Department", getDept())
.add("EmployeeID", this.getEmpID()).toString();
}@Override
protected void finalize() throws Throwable
{
System.out.println("The name " + getName() + " will be GC.");
}
}
package com.example.springboottest.guava_cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.Weigher;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class _1_CacheLoaderBasicTest {
public static void main(String[] args) throws Exception{
// test1();
// test2();
// test3();
// test4();
test5();
}
/**測試軟引用在緩存中被過期
* 需要設置堆內存信息
* -Xms64M -Xmx64M -XX:+PrintGCDetails
* @throws Exception
*/
private static void test5() throws Exception{
LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
.expireAfterWrite(200, TimeUnit.SECONDS)
.softValues()//只能對values 做軟引用處理
.build(createCacheLoader2());
int i = 0;
//Employee 內部會有一個1M的數(shù)組蓖墅,所以一個Employee對象 至少占1M空間
//由于軟引用在內存快不足的時候會被回收 所以這里運行結果 可以創(chuàng)建很多對象 不止64個
//todo 但是考慮到軟引用的性能影響 一般我們在在緩存中設置size來限制緩存所占內存的總大小 而不是使用軟引用來
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ù)據(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 此時存活
TimeUnit.MILLISECONDS.sleep(900);
guava = cache.getIfPresent("Guava");
System.out.println(guava);//1.90s guava 此時依然存活
TimeUnit.SECONDS.sleep(1);
guava = cache.getIfPresent("Guava");
System.out.println(guava);//2.99s guava 此時不存活
}
//測試Access的expire模式下的緩存存活情況
private static void test2() throws Exception{
LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
//expireAfterAccess,access:包括讀(read),寫(write),改(update),都會續(xù)長緩存的存活期
.expireAfterAccess(2, TimeUnit.SECONDS)
.build(createCacheLoader2());
cache.getUnchecked("Alex");
TimeUnit.SECONDS.sleep(3);
Employee alex = cache.getIfPresent("Alex");//睡眠了 3s 此時緩存獲取不到數(shù)據(jù)
System.out.println(alex);
cache.getUnchecked("Guava");
TimeUnit.SECONDS.sleep(1);
Employee employee = cache.getIfPresent("Guava");//此時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);
}
//測試基礎用法
public static void test1() throws ExecutionException, InterruptedException {
//創(chuàng)建一個緩存容器對象 其最大容量是3,容器內元素存放30ms就過期
LoadingCache<String, Employee> cache = CacheBuilder.newBuilder()
.maximumSize(3)
.expireAfterAccess(30, TimeUnit.MILLISECONDS)
.build(createCacheLoader());
//#################################### 測試30ms時間過期 ####################################
// System.out.println(cache.get("Alex").getName());
// TimeUnit.MILLISECONDS.sleep(31);
// System.out.println(cache.get("Alex").getName());
//測試到達容量之后 LRU過期
//#################################### 測試到達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());//此時Alex被過期調,再拿Alex的話會從DB中拿
// System.out.println(cache.size());
// System.out.println(cache.get("Alex").getName());
// System.out.println();
//#################################### 測試到達weight之后被LRU過期(過期策略:自定義) ####################################
//設置一個稱重器 稱重的方式是:對象的重量weight = name的長度 + empId的長度 + Dept的長度
Weigher<String, Employee> weigher = (key, employee) ->
employee.getName().length() + employee.getEmpID().length() + employee.getDept().length();
//設置緩存重量限制為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)達到45氮帐,此時Gavin被過期
Employee employee = cache2.getIfPresent("Gavin");//此時再去緩存中拿Gavin 是拿不到的
System.out.println(employee);
//#################################### LoadingCache 的一些api ####################################
// 從LoadingCache中拿數(shù)據(jù)谨胞,如果拿不到,會去從DB中拿蹈矮,
// cache.get("aa");
// 與get()方法的區(qū)別是這里不需要顯式捕獲異常
// cache.getUnchecked("aa");
// 從緩存中拿key對應的數(shù)據(jù)砰逻,如果緩存中沒有 就返回null,不會去從DB中拿
// cache.getIfPresent("aa");
}
private static CacheLoader<String, Employee> createCacheLoader() {
return new CacheLoader<String, Employee>() {
@Override
public Employee load(String key) throws Exception {
// 這里要注意:load的時候不能返回空泛鸟, 那返回空了怎么辦蝠咆?很有可能命中不了
// 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()來構建緩存
return CacheLoader.from(key -> new Employee(key, key, key));
}
}