日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

自己实现简单Java缓存类

發布時間:2023/12/10 java 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 自己实现简单Java缓存类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

需求分析

項目中經常會遇到這種場景:一份數據需要在多處共享,有些數據還有時效性,過期自動失效。比如手機驗證碼,發送之后需要緩存起來,然后處于安全性考慮,一般還要設置有效期,到期自動失效。我們怎么實現這樣的功能呢?

解決方案

  • 使用現有的緩存技術框架,比如redis,ehcache。優點:成熟,穩定,功能強大;缺點,項目需要引入對應的框架,不夠輕量。
  • 如果不考慮分布式,只是在單線程或者多線程間作數據緩存,其實完全可以自己手寫一個緩存工具。下面就來簡單實現一個這樣的工具。

  • 代碼

    import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit;/*** @Author: lixk* @Date: 2018/5/9 15:03* @Description: 簡單的內存緩存工具類*/ public class Cache {/*** 鍵值對集合*/private final static Map<String, Entity> map = new HashMap<>();/*** 定時器線程池,用于清除過期緩存*/private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();/*** 添加緩存** @param key 鍵* @param data 值*/public synchronized static void put(String key, Object data) {Cache.put(key, data, 0);}/*** 添加緩存** @param key 鍵* @param data 值* @param expire 過期時間,單位:毫秒, 0表示無限長*/public synchronized static void put(String key, Object data, long expire) {//清除原鍵值對Cache.remove(key);//設置過期時間if (expire > 0) {Future future = executor.schedule(new Runnable() {@Overridepublic void run() {//過期后清除該鍵值對synchronized (Cache.class) {map.remove(key);}}}, expire, TimeUnit.MILLISECONDS);map.put(key, new Entity(data, future));} else {//不設置過期時間map.put(key, new Entity(data, null));}}/*** 讀取緩存** @param key 鍵* @return*/public synchronized static <T> T get(String key) {Entity entity = map.get(key);return entity == null ? null : (T) entity.value;}/*** 清除緩存** @param key 鍵* @return*/public synchronized static <T> T remove(String key) {//清除原緩存數據Entity entity = map.remove(key);if (entity == null) {return null;}//清除原鍵值對定時器if (entity.future != null) {entity.future.cancel(true);}return (T) entity.value;}/*** 查詢當前緩存的鍵值對數量** @return*/public synchronized static int size() {return map.size();}/*** 緩存實體類*/private static class Entity {/*** 鍵值對的value*/public Object value;/*** 定時器Future*/public Future future;public Entity(Object value, Future future) {this.value = value;this.future = future;}} }

    說明

    本工具類主要采用HashMap+定時器線程池實現,map用于存儲鍵值對數據,map的value是Cache的內部類對象Entity,Entity包含value和該鍵值對的生命周期定時器Future。Cache類對外只提供了幾個同步方法:

    方法作用
    put(key, value)插入緩存數據
    put(key, value, expire)插入帶過期時間的緩存數據, expire: 過期時間,單位:毫秒
    get(key)獲取緩存數據
    remove(key)刪除緩存數據
    size()查詢當前緩存記錄數

    當添加鍵值對數據的時候,首先會調用remove()方法,清除掉原來相同key的數據,并取消對應的定時清除任務,然后添加新數據到map中,并且,如果設置了有效時間,則添加對應的定時清除任務到定時器線程池。

    測試

    測試代碼如下:

    import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger;/*** @Author: lixk* @Date: 2018/5/9 16:40* @Description: 緩存工具類測試*/ public class CacheTest {/*** 測試** @param args*/public static void main(String[] args) throws InterruptedException {String key = "id";//不設置過期時間System.out.println("***********不設置過期時間**********");Cache.put(key, 123);System.out.println("key:" + key + ", value:" + Cache.get(key));System.out.println("key:" + key + ", value:" + Cache.remove(key));System.out.println("key:" + key + ", value:" + Cache.get(key));//設置過期時間System.out.println("\n***********設置過期時間**********");Cache.put(key, "123456", 1000);System.out.println("key:" + key + ", value:" + Cache.get(key));Thread.sleep(2000);System.out.println("key:" + key + ", value:" + Cache.get(key));System.out.println("\n***********100w讀寫性能測試************");//創建有10個線程的線程池,將1000000次操作分10次添加到線程池int threads = 10;ExecutorService pool = Executors.newFixedThreadPool(threads);//每批操作數量int batchSize = 100000;//添加{CountDownLatch latch = new CountDownLatch(threads);AtomicInteger n = new AtomicInteger(0);long start = System.currentTimeMillis();for (int t = 0; t < threads; t++) {pool.submit(() -> {for (int i = 0; i < batchSize; i++) {int value = n.incrementAndGet();Cache.put(key + value, value, 300000);}latch.countDown();});}//等待全部線程執行完成,打印執行時間latch.await();System.out.printf("添加耗時:%dms\n", System.currentTimeMillis() - start);}//查詢{CountDownLatch latch = new CountDownLatch(threads);AtomicInteger n = new AtomicInteger(0);long start = System.currentTimeMillis();for (int t = 0; t < threads; t++) {pool.submit(() -> {for (int i = 0; i < batchSize; i++) {int value = n.incrementAndGet();Cache.get(key + value);}latch.countDown();});}//等待全部線程執行完成,打印執行時間latch.await();System.out.printf("查詢耗時:%dms\n", System.currentTimeMillis() - start);}System.out.println("當前緩存容量:" + Cache.size());} }

    測試結果

    ***********不設置過期時間********** key:id, value:123 key:id, value:123 key:id, value:null***********設置過期時間********** key:id, value:123456 key:id, value:null***********100w讀寫性能測試************ 添加耗時:1729ms 查詢耗時:283ms 當前緩存容量:1000000

    測試程序使用有10個線程的線程池來模擬并發,分別執行一百萬次添加和查詢操作,時間大約在兩秒左右,表現還不錯,每秒近百萬讀寫應該還是可以滿足大多數高并發場景的 ^^


    備注:如果對緩存失效延遲非常敏感,不能容忍百萬數據亞秒級失效延遲,必須保證嚴格失效時間的話,可以參考另一版實現(數據實體加入了過期時間,每次取出數據時會先做判斷)。地址:https://github.com/lixk/ECache

    總結

    以上是生活随笔為你收集整理的自己实现简单Java缓存类的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。