學習使用Guava Cache

  官方文檔:https://github.com/google/guava/wiki/CachesExplainedjava

 

目錄

  1、guava cache介紹git

  2、快速入門github

    2.一、引入依賴redis

    2.二、第一個示例spring

    2.三、批量操做緩存

  3、拓展swoole

    3.一、移除監聽器ide

    3.二、刷新緩存ui

    3.三、自定義刷新的操做google

 

 

1、guava cache介紹

  對於常見的cache(緩存),好比memecache、redis、tair這些中間件,也有jdk的一些類庫能夠當作緩存使用,好比實現了ConcurrentMap接口的ConcurrentHashMap。

  ConcurrentHashMap雖然可讓咱們在程序中緩存一些數據,可是這些數據其實永遠不會過時,除非手動刪除,不然數據將一直存在於內存中。

  而guava cache(LocalCache,本地緩存),也是實現了ConcurrentMap的類,但他和ConcurrentHashMap的區別在於,guava cache能夠設置數據過時以及淘汰機制,更加符合通常的應用需求。

  guava cache,是一個本地緩存,也就是說,他的數據是存放在機器內存,這個機器,就是JVM所在的機器(會佔用一部份內存),而不是像redis那樣專門作緩存的機器。

 

2、快速入門

2.一、引入依賴

  guava cahce是guava的子模塊。

  guava倉庫地址:https://mvnrepository.com/artifact/com.google.guava/guava

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.0-jre</version>
</dependency>

  

2.二、第一個示例

  下面是一個簡單的示例:

package cn.ganlixin.guava;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.junit.Test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class UseCache {

    @Test
    public void testLoadingCache() throws ExecutionException {
        // 建立緩存(容器)
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                // 數據寫入1分鐘後失效
                .expireAfterWrite(1, TimeUnit.MINUTES)
                // 支持緩存項的數據最大爲3個
                .maximumSize(3)
                // 實現CacheLoader,當在緩存中未找到所需的緩存項時,會執行CacheLoader的load方法加載緩存。
                .build(new CacheLoader<String, String>() {
                    // 方法的返回值會被緩存,注意,返回null時,會拋異常
                    @Override
                    public String load(String key) throws Exception {
                        System.out.println("key:" + key + " 未找到,開始加載....");
                        return key + "-" + key;
                    }
                });

        // 加入緩存
        cache.put("Java", "spring");
        cache.put("PHP", "swoole");

        // 從緩存中獲取值
        String res1 = cache.get("Java"); // get方法須要拋出ExecutionException異常
        // cache.getUnchecked("Java"); // 功能和get同樣,可是不會throw異常
        System.out.println(res1);
        // 輸出:spring

        // 緩存中爲存放key爲Golang的緩存項,因此進行加載
        String res2 = cache.get("Golang");
        System.out.println(res2);
        // key:Golang 未找到,開始加載....
        // Golang-Golang

        // Golang的緩存項前面已經加載過了,且沒有被清除,因此此次直接獲取到對應的value
        String res3 = cache.get("Golang");
        System.out.println(res3);
        // Golang-Golang

        // 前面添加了3個緩存項(已經達到設置上限,此時再添加緩存項,將會觸發淘汰)
        cache.put("Node", "KOA");
        System.out.println(cache.get("PHP")); // PHP在cache中已被淘汰
        // key:PHP 未找到,開始加載....
        // PHP-PHP

        // 查詢緩存,若是未找到,不會觸發加載操做,而是範圍null。
        String res4 = cache.getIfPresent("key");
        System.out.println(res4); // null
    }
}  

  

 

2.三、批量操做

  guava cache支持批量添加、查詢、清除操做。

package cn.ganlixin.guava;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import org.junit.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class UseCache {

    @Test
    public void testMultiple() throws ExecutionException {
        // 建立緩存
        LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder()
                .maximumSize(2) // 最大緩存項數量爲2
                .expireAfterWrite(10, TimeUnit.SECONDS) // 數據寫入10秒過時
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        System.out.println("key:" + key + " 未找到,開始加載....");
                        return key + "-" + key;
                    }
                });

        Map<String, String> map = new HashMap<>();
        map.put("one", "111111");
        map.put("two", "222222");
        map.put("three", "3333333");
        // 批量添加
        loadingCache.putAll(map);

        List<String> keys = new ArrayList<>();
        keys.add("one");
        keys.add("two");
        // 批量獲取
        ImmutableMap<String, String> allData = loadingCache.getAll(keys);
        System.out.println(allData);
        // {one=one-one, two=222222}

        // 批量清除
        loadingCache.invalidateAll(keys);
        loadingCache.invalidateAll(); // 全量清除
    }
}

  

3、拓展

3.一、移除監聽器

  移除監聽器,是指監聽緩存項,當緩存項被清除時,執行指定對操做。

package cn.ganlixin.guava;

import com.google.common.cache.*;
import org.junit.Test;

import java.util.concurrent.TimeUnit;

public class UseCache {

    @Test
    public void testRemoveListender() {

        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .expireAfterWrite(1, TimeUnit.MINUTES)
                // 綁定"移除監聽器",當元素被清除時,執行onRemoval方法
                .removalListener(new RemovalListener<String, String>() {
                    @Override
                    public void onRemoval(RemovalNotification removalNotification) {
                        Object key = removalNotification.getKey();
                        Object val = removalNotification.getValue();
                        System.out.println("被清除的元素,key:" + key + ", val:" + val);

                    }
                })
                // 當在緩存中未找到key時,執行load操做。
                .build(new CacheLoader<String, String>() {
                    // 當key未找到時,執行的加載操做
                    @Override
                    public String load(String key) throws Exception {
                        System.out.println("未找到key:" + key + ",開始加載...");
                        return key + key;
                    }
                });

        cache.put("one", "111111");
        cache.put("two", "123456");

        // 繼續添加元素,會觸發清理操做,觸發移除監聽器
        cache.put("three", "2233333");
        // 輸出:被清除的元素,key:one, val:111111

        String res = cache.getUnchecked("four");
        System.out.println(res);
        // 輸出 
        // 未找到key:four,開始加載...
        // 被清除的元素,key:two, val:123456
        // fourfour
    }
}

  

3.二、刷新緩存

  刷新緩存,是指一個緩存項寫入緩存後,通過一段時間(設置的刷新間隔),再次訪問該緩存項的時候,會刷新該緩存項,能夠理解爲將該項的key取出,執行CacheLoader的load方法,而後將返回值替換舊的值。

  能夠在建立緩存的時候,設置刷新緩存的時間:

package cn.ganlixin.guava;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.junit.Test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class UseCache {

    @Test
    public void testRefresh() throws InterruptedException, ExecutionException {
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                // 設置刷新的時機
                .refreshAfterWrite(2, TimeUnit.SECONDS)
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        System.out.println("key:" + key + " 未找到,開始加載....");
                        return key + "-" + key;
                    }
                });

        cache.put("one", "11111");
        cache.put("two", "22222");

        // 休眠3秒
        Thread.sleep(3000L);

        System.out.println(cache.get("one"));
        //key:one 未找到,開始加載....
        //one-one

        System.out.println(cache.get("two"));
        //key:two 未找到,開始加載....
        //two-two
    }
}

  須要注意的是,以上面代碼爲例,並非設置寫入2秒後,就會被刷新,而是當寫入2秒後,且再次被訪問時,纔會被刷新;若是一個緩存項寫入的時間超過2秒,可是一直沒有訪問該項,那麼這一項是不會被刷新的。這與Memecache和Redis的原理相似。

  

3.三、自定義刷新緩存的操做

  前面內容中,提到可使用設置refreshAfterWrite()設置數據寫入多久後,再次被訪問時,會被刷新,其實,咱們能夠對於刷新操做自定義,只須要重寫CacheLoader的reload方法便可。

package cn.ganlixin.guava;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ListenableFuture;
import org.junit.Test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class UseCache {

    @Test
    public void testReload() throws InterruptedException {
        LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                // 設置刷新的時機
                .refreshAfterWrite(2, TimeUnit.SECONDS)
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        System.out.println("key:" + key + " 未找到,開始加載....");
                        return key + "-" + key;
                    }

                    // 刷新緩存時,執行的操做
                    @Override
                    public ListenableFuture<String> reload(String key, String oldValue) throws Exception {
                        System.out.println("刷新緩存項,key:" + key + ", oldValue:" + oldValue);
                        return super.reload(key, oldValue);
                    }
                });

        cache.put("hello", "world");
        Thread.sleep(3000L);

        System.out.println(cache.getUnchecked("hello"));
        // 刷新緩存項,key:hello, oldValue:world
        // key:hello 未找到,開始加載....
        // hello-hello
    }
}
相關文章
相關標籤/搜索