日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

自己实现简单Java缓存类

發(fā)布時(shí)間:2023/12/10 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 自己实现简单Java缓存类 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

需求分析

項(xiàng)目中經(jīng)常會(huì)遇到這種場(chǎng)景:一份數(shù)據(jù)需要在多處共享,有些數(shù)據(jù)還有時(shí)效性,過(guò)期自動(dòng)失效。比如手機(jī)驗(yàn)證碼,發(fā)送之后需要緩存起來(lái),然后處于安全性考慮,一般還要設(shè)置有效期,到期自動(dòng)失效。我們?cè)趺磳?shí)現(xiàn)這樣的功能呢?

解決方案

  • 使用現(xiàn)有的緩存技術(shù)框架,比如redis,ehcache。優(yōu)點(diǎn):成熟,穩(wěn)定,功能強(qiáng)大;缺點(diǎn),項(xiàng)目需要引入對(duì)應(yīng)的框架,不夠輕量。
  • 如果不考慮分布式,只是在單線程或者多線程間作數(shù)據(jù)緩存,其實(shí)完全可以自己手寫(xiě)一個(gè)緩存工具。下面就來(lái)簡(jiǎn)單實(shí)現(xiàn)一個(gè)這樣的工具。

  • 代碼

    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: 簡(jiǎn)單的內(nèi)存緩存工具類(lèi)*/ public class Cache {/*** 鍵值對(duì)集合*/private final static Map<String, Entity> map = new HashMap<>();/*** 定時(shí)器線程池,用于清除過(guò)期緩存*/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 過(guò)期時(shí)間,單位:毫秒, 0表示無(wú)限長(zhǎng)*/public synchronized static void put(String key, Object data, long expire) {//清除原鍵值對(duì)Cache.remove(key);//設(shè)置過(guò)期時(shí)間if (expire > 0) {Future future = executor.schedule(new Runnable() {@Overridepublic void run() {//過(guò)期后清除該鍵值對(duì)synchronized (Cache.class) {map.remove(key);}}}, expire, TimeUnit.MILLISECONDS);map.put(key, new Entity(data, future));} else {//不設(shè)置過(guò)期時(shí)間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) {//清除原緩存數(shù)據(jù)Entity entity = map.remove(key);if (entity == null) {return null;}//清除原鍵值對(duì)定時(shí)器if (entity.future != null) {entity.future.cancel(true);}return (T) entity.value;}/*** 查詢(xún)當(dāng)前緩存的鍵值對(duì)數(shù)量** @return*/public synchronized static int size() {return map.size();}/*** 緩存實(shí)體類(lèi)*/private static class Entity {/*** 鍵值對(duì)的value*/public Object value;/*** 定時(shí)器Future*/public Future future;public Entity(Object value, Future future) {this.value = value;this.future = future;}} }

    說(shuō)明

    本工具類(lèi)主要采用HashMap+定時(shí)器線程池實(shí)現(xiàn),map用于存儲(chǔ)鍵值對(duì)數(shù)據(jù),map的value是Cache的內(nèi)部類(lèi)對(duì)象Entity,Entity包含value和該鍵值對(duì)的生命周期定時(shí)器Future。Cache類(lèi)對(duì)外只提供了幾個(gè)同步方法:

    方法作用
    put(key, value)插入緩存數(shù)據(jù)
    put(key, value, expire)插入帶過(guò)期時(shí)間的緩存數(shù)據(jù), expire: 過(guò)期時(shí)間,單位:毫秒
    get(key)獲取緩存數(shù)據(jù)
    remove(key)刪除緩存數(shù)據(jù)
    size()查詢(xún)當(dāng)前緩存記錄數(shù)

    當(dāng)添加鍵值對(duì)數(shù)據(jù)的時(shí)候,首先會(huì)調(diào)用remove()方法,清除掉原來(lái)相同key的數(shù)據(jù),并取消對(duì)應(yīng)的定時(shí)清除任務(wù),然后添加新數(shù)據(jù)到map中,并且,如果設(shè)置了有效時(shí)間,則添加對(duì)應(yīng)的定時(shí)清除任務(wù)到定時(shí)器線程池。

    測(cè)試

    測(cè)試代碼如下:

    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: 緩存工具類(lèi)測(cè)試*/ public class CacheTest {/*** 測(cè)試** @param args*/public static void main(String[] args) throws InterruptedException {String key = "id";//不設(shè)置過(guò)期時(shí)間System.out.println("***********不設(shè)置過(guò)期時(shí)間**********");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));//設(shè)置過(guò)期時(shí)間System.out.println("\n***********設(shè)置過(guò)期時(shí)間**********");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讀寫(xiě)性能測(cè)試************");//創(chuàng)建有10個(gè)線程的線程池,將1000000次操作分10次添加到線程池int threads = 10;ExecutorService pool = Executors.newFixedThreadPool(threads);//每批操作數(shù)量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();});}//等待全部線程執(zhí)行完成,打印執(zhí)行時(shí)間latch.await();System.out.printf("添加耗時(shí):%dms\n", System.currentTimeMillis() - start);}//查詢(xún){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();});}//等待全部線程執(zhí)行完成,打印執(zhí)行時(shí)間latch.await();System.out.printf("查詢(xún)耗時(shí):%dms\n", System.currentTimeMillis() - start);}System.out.println("當(dāng)前緩存容量:" + Cache.size());} }

    測(cè)試結(jié)果

    ***********不設(shè)置過(guò)期時(shí)間********** key:id, value:123 key:id, value:123 key:id, value:null***********設(shè)置過(guò)期時(shí)間********** key:id, value:123456 key:id, value:null***********100w讀寫(xiě)性能測(cè)試************ 添加耗時(shí):1729ms 查詢(xún)耗時(shí):283ms 當(dāng)前緩存容量:1000000

    測(cè)試程序使用有10個(gè)線程的線程池來(lái)模擬并發(fā),分別執(zhí)行一百萬(wàn)次添加和查詢(xún)操作,時(shí)間大約在兩秒左右,表現(xiàn)還不錯(cuò),每秒近百萬(wàn)讀寫(xiě)應(yīng)該還是可以滿(mǎn)足大多數(shù)高并發(fā)場(chǎng)景的 ^^


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

    總結(jié)

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

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。