google guava cache緩存基本使用講解

来源:http://www.cnblogs.com/vikde/archive/2017/12/15/8045226.html
-Advertisement-
Play Games

代碼地址:https://github.com/vikde/demo-guava-cache 一、簡介 guava cache是google guava中的一個記憶體緩存模塊,用於將數據緩存到JVM記憶體中.實際項目開發中經常將一些比較公共或者常用的數據緩存起來方便快速訪問. 記憶體緩存最常見的就是基於H ...


代碼地址:https://github.com/vikde/demo-guava-cache

一、簡介

guava cache是google guava中的一個記憶體緩存模塊,用於將數據緩存到JVM記憶體中.實際項目開發中經常將一些比較公共或者常用的數據緩存起來方便快速訪問.

記憶體緩存最常見的就是基於HashMap實現的緩存,為瞭解決併發問題也可能也會用到ConcurrentHashMap等併發集合,但是記憶體緩存需要考慮很多問題,包括併發問題、緩存過期機制、緩存移除機制、緩存命中統計率等.

guava cache已經考慮到這些問題,可以上手即用.通過CacheBuilder創建緩存、然後設置緩存的相關參數、設置緩存的載入方法等.本例子主要講解guava cache的基本用法,詳細的說明已在代碼中說明.

 

二、代碼示例

 1 package com.vikde.demo.guava.cache;
 2 
 3 import com.google.common.cache.CacheBuilder;
 4 import com.google.common.cache.CacheLoader;
 5 import com.google.common.cache.LoadingCache;
 6 
 7 import java.text.SimpleDateFormat;
 8 import java.util.Date;
 9 import java.util.Random;
10 import java.util.concurrent.TimeUnit;
11 
12 /**
13  * google guava cache 緩存demo
14  *
15  * @author vikde
16  * @date 2017/12/14
17  */
18 public class DemoGuavaCache {
19     public static void main(String[] args) throws Exception {
20         LoadingCache<Integer, String> cache = CacheBuilder.newBuilder()
21                                                           //設置併發級別為8,併發級別是指可以同時寫緩存的線程數
22                                                           .concurrencyLevel(8)
23                                                           //設置緩存容器的初始容量為10
24                                                           .initialCapacity(10)
25                                                           //設置緩存最大容量為100,超過100之後就會按照LRU最近雖少使用演算法來移除緩存項
26                                                           .maximumSize(100)
27                                                           //是否需要統計緩存情況,該操作消耗一定的性能,生產環境應該去除
28                                                           .recordStats()
29                                                           //設置寫緩存後n秒鐘過期
30                                                           .expireAfterWrite(17, TimeUnit.SECONDS)
31                                                           //設置讀寫緩存後n秒鐘過期,實際很少用到,類似於expireAfterWrite
32                                                           //.expireAfterAccess(17, TimeUnit.SECONDS)
33                                                           //只阻塞當前數據載入線程,其他線程返回舊值
34                                                           //.refreshAfterWrite(13, TimeUnit.SECONDS)
35                                                           //設置緩存的移除通知
36                                                           .removalListener(notification -> {
37                                                               System.out.println(notification.getKey() + " " + notification.getValue() + " 被移除,原因:" + notification.getCause());
38                                                           })
39                                                           //build方法中可以指定CacheLoader,在緩存不存在時通過CacheLoader的實現自動載入緩存
40                                                           .build(new DemoCacheLoader());
41 
42         //模擬線程併發
43         new Thread(() -> {
44             //非線程安全的時間格式化工具
45             SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
46             try {
47                 for (int i = 0; i < 15; i++) {
48                     String value = cache.get(1);
49                     System.out.println(Thread.currentThread().getName() + " " + simpleDateFormat.format(new Date()) + " " + value);
50                     TimeUnit.SECONDS.sleep(3);
51                 }
52             } catch (Exception ignored) {
53             }
54         }).start();
55 
56         new Thread(() -> {
57             SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
58             try {
59                 for (int i = 0; i < 10; i++) {
60                     String value = cache.get(1);
61                     System.out.println(Thread.currentThread().getName() + " " + simpleDateFormat.format(new Date()) + " " + value);
62                     TimeUnit.SECONDS.sleep(5);
63                 }
64             } catch (Exception ignored) {
65             }
66         }).start();
67         //緩存狀態查看
68         System.out.println(cache.stats().toString());
69     }
70 
71     /**
72      * 隨機緩存載入,實際使用時應實現業務的緩存載入邏輯,例如從資料庫獲取數據
73      */
74     public static class DemoCacheLoader extends CacheLoader<Integer, String> {
75         @Override
76         public String load(Integer key) throws Exception {
77             System.out.println(Thread.currentThread().getName() + " 載入數據開始");
78             TimeUnit.SECONDS.sleep(8);
79             Random random = new Random();
80             System.out.println(Thread.currentThread().getName() + " 載入數據結束");
81             return "value:" + random.nextInt(10000);
82         }
83     }
84 }

 

三、策略分析

expireAfterWrite 寫緩存後多久過期
expireAfterAccess 讀寫緩存後多久過期
refreshAfterWrite 寫入數據後多久過期,只阻塞當前數據載入線程,其他線程返回舊值

這幾個策略時間可以單獨設置,也可以組合配置

expireAfterWrite與refreshAfterWrite單獨使用與混合使用的策略分析

已知配置條件:
Thread-1 每 3 秒獲取一次緩存id=1的數據
Thread-2 每 5 秒獲取一次緩存id=1的數據
載入一次緩存載入數據耗時 8 秒

1、expireAfterWrite單獨使用

expireAfterWrite=17

Thread-2 載入數據開始
Thread-2 載入數據結束
Thread-1 01:04:07 value:6798
Thread-2 01:04:07 value:6798
Thread-1 01:04:10 value:6798
Thread-2 01:04:12 value:6798
Thread-1 01:04:13 value:6798
Thread-1 01:04:16 value:6798
Thread-2 01:04:17 value:6798
Thread-1 01:04:19 value:6798
Thread-1 01:04:22 value:6798
Thread-2 01:04:22 value:6798
1 value:6798 被移除,原因:EXPIRED
Thread-1 載入數據開始
Thread-1 載入數據結束
Thread-1 01:04:33 value:7836
Thread-2 01:04:33 value:7836
Thread-1 01:04:36 value:7836
Thread-2 01:04:38 value:7836
Thread-1 01:04:39 value:7836

說明:

啟動時Thread-2載入數據,此時緩存中無數據,Thread-1阻塞等待Thread-2載入完成數據. 在設置的時間數據過期後Thread-1載入數據,Thread-2本應該01:04:22後的5秒載入數據,但是Thread-1等待3秒後載入,數據載入耗時8秒,所以Thread-2在01:04:33時載入數據成功.

結論:

當其他線程在載入數據的時候,當前線程會一直阻塞等待其他線程載入數據完成.

2、refreshAfterWrite單獨使用

refreshAfterWrite=17

Thread-2 載入數據開始
Thread-2 載入數據結束
Thread-1 01:13:32 value:551
Thread-2 01:13:32 value:551
Thread-1 01:13:35 value:551
Thread-2 01:13:37 value:551
Thread-1 01:13:38 value:551
Thread-1 01:13:41 value:551
Thread-2 01:13:42 value:551
Thread-1 01:13:44 value:551
Thread-1 01:13:47 value:551
Thread-2 01:13:47 value:551
Thread-1 載入數據開始
Thread-2 01:13:52 value:551
Thread-2 01:13:57 value:551
Thread-1 載入數據結束
1 value:551 被移除,原因:REPLACED
Thread-1 01:13:58 value:827
Thread-1 01:14:01 value:827
Thread-2 01:14:02 value:827
Thread-1 01:14:04 value:827
Thread-2 01:14:07 value:827

說明:

啟動時Thread-2載入數據,此時緩存中無數據,Thread-1阻塞等待Thread-2載入完成數據. 在設置的時間數據過期後Thread-1載入數據,Thread-2仍然按照策略獲取到舊數據成功.

結論:

當沒有數據的時候,其他線程在載入數據的時候,當前線程會一直阻塞等待其他線程載入數據完成;如果有數據的情況下其他線程正在載入數據,當前線程返回舊數據.

3、expireAfterWrite與refreshAfterWrite一起使用情況一

expireAfterWrite=13

refreshAfterWrite=17

Thread-2 載入數據開始
Thread-2 載入數據結束
Thread-1 01:18:32 value:5901
Thread-2 01:18:32 value:5901
Thread-1 01:18:35 value:5901
Thread-2 01:18:37 value:5901
Thread-1 01:18:38 value:5901
Thread-1 01:18:41 value:5901
Thread-2 01:18:42 value:5901
Thread-1 01:18:44 value:5901
1 value:5901 被移除,原因:EXPIRED
Thread-1 載入數據開始
Thread-1 載入數據結束
Thread-2 01:18:55 value:1300
Thread-1 01:18:55 value:1300
Thread-1 01:18:58 value:1300
Thread-2 01:19:00 value:1300
Thread-1 01:19:01 value:1300

說明:

啟動時Thread-2載入數據,此時緩存中無數據,Thread-1阻塞等待Thread-2載入完成數據. 在設置的時間數據過期後Thread-1載入數據,Thread-2本應該01:18:42後的5秒載入數據,但是Thread-1等待3秒後載入,數據載入耗時8秒,所以Thread-2在01:18:55時載入數據成功.

結論:

當其他線程在載入數據的時候,當前線程會一直阻塞等待其他線程載入數據完成,與單獨使用expireAfterWrite一樣的效果.

4、expireAfterWrite與refreshAfterWrite一起使用情況二

expireAfterWrite=17

refreshAfterWrite=13

Thread-2 載入數據開始
Thread-2 載入數據結束
Thread-1 01:20:25 value:1595
Thread-2 01:20:25 value:1595
Thread-1 01:20:28 value:1595
Thread-2 01:20:30 value:1595
Thread-1 01:20:31 value:1595
Thread-1 01:20:34 value:1595
Thread-2 01:20:35 value:1595
Thread-1 01:20:37 value:1595
Thread-2 載入數據開始
Thread-1 01:20:40 value:1595
Thread-2 載入數據結束
Thread-1 01:20:48 value:2277
1 value:1595 被移除,原因:EXPIRED
Thread-2 01:20:48 value:2277
Thread-1 01:20:51 value:2277
Thread-2 01:20:53 value:2277
Thread-1 01:20:54 value:2277
Thread-1 01:20:57 value:2277
Thread-2 01:20:58 value:2277
Thread-1 01:21:00 value:2277
Thread-1 載入數據開始
Thread-2 01:21:03 value:2277
Thread-1 載入數據結束
Thread-2 01:21:11 value:3750
1 value:2277 被移除,原因:EXPIRED
Thread-1 01:21:11 value:3750
Thread-1 01:21:14 value:3750
Thread-2 01:21:16 value:3750
Thread-1 01:21:17 value:3750
Thread-1 01:21:20 value:3750
Thread-2 01:21:21 value:3750

說明:

啟動時Thread-2載入數據,此時緩存中無數據,Thread-1阻塞等待Thread-2載入完成數據. 在設置的時間數據過期後Thread-2載入數據,Thread-1仍然按照策略在01:20:40獲取到舊數據成功,但是本應該01:20:45繼續獲取一次數據但是等到01:20:48才獲取成功.

結論:

當沒有數據的時候,其他線程在載入數據的時候,當前線程會一直阻塞等待其他線程載入數據完成; 如果有數據的情況下其他線程正在載入數據,已經超過refreshAfterWrite設置時間但是沒有超過expireAfterWrite設置的時間時當前線程返回舊數據. 如果有數據的情況下其他線程正在載入數據,已經超過expireAfterWrite設置的時間時當前線程阻塞等待其他線程載入數據完成. 這種情況適合與設置一個載入緩衝區的情況,既能保證過期後載入數據,又能保證長時間沒訪問多個線程併發時獲取到過期舊數據的情況.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Js阻塞機制,跟Js引擎的單線程處理方式有關,每個window一個JS線程。所謂單線程,在某個特定的時刻只有特定的代碼能夠被執行,並阻塞其它的代碼。 由於瀏覽器是事件驅動的(Event driven),因此瀏覽器中很多行為是非同步(Asynchronized)的,很容易有事件被同時或者連續觸發。當非同步 ...
  • accept表示打開的系統文件目錄;capture表示的是系統所捕獲的預設設備,camera:照相機;camcorder:攝像機;microphone:照相+攝像。 如果不加上capture,則只會顯示相應的,例如上述三種依次是:拍照或圖庫,錄像或圖庫,錄像或拍照或圖庫,加上capture之後不會調 ...
  • javascrpit面向對象之綜合 這一章是對前幾章的一個總結,通過一個案例來綜合認識javascript面向對象的基本用法 需求: 幾乎所有的web應用都需要保存數據一些到本地,那麼我們就來做一個數據儲存器吧。 詳細需求需求: 當本地儲存有數據時,取用本地的數據,沒有時使用預設的數據 判斷本地的數 ...
  • REST即表述性狀態傳遞(英文:Representational State Transfer,簡稱REST)是Roy Fielding博士在2000年他的博士論文中提出來的一種軟體架構風格 ...
  • HP-Socket是一套通用的高性能TCP/UDP/HTTP 通信框架,包含服務端組件、客戶端組件和Agent組件,廣泛適用於各種不同應用場景的TCP/UDP/HTTP通信系統,提供C/C++、C#、Delphi、E(易語言)、Java、Python等編程語言介面。HP-Socket對通信層完全封裝... ...
  • 集群架構 Hadoop的安裝其實就是HDFS和YARN集群的配置,從下麵的架構圖可以看出,HDFS的每一個DataNode都需要配置NameNode的位置。同理YARN中的每一個NodeManager都需要配置ResourceManager的位置。 NameNode和ResourceManager的 ...
  • 一、相關知識點回顧 1、什麼是反射? 可以用字元串的方式去訪問對象的屬性 2、反射有四種方法? 3、創建ModelForm的兩種方式 二、具體流程以及相關知識點 1、路由系統 讓url對應視圖,這時的視圖可以是一個元組,元組的裡面放三個參數,第一個是個列表,這兩種表示方式都是一樣的,用那種都行 na ...
  • Boss直聘'崗位'和'招聘信息'數據開源列表 OS:MacBook Pro , macOS High Sierra 10.13.2 Database:MongoDB v3.4.7 Mongo可視化工具:MongoBooster v4.1.3 數據都是用MongoBooster導出來的,如果用不了, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...