电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

Java構建高效結果緩存方法示例

瀏覽:117日期:2022-09-02 13:16:37

緩存是現代應用服務器中非常常用的組件。除了第三方緩存以外,我們通常也需要在java中構建內部使用的緩存。那么怎么才能構建一個高效的緩存呢? 本文將會一步步的進行揭秘。

使用HashMap

緩存通常的用法就是構建一個內存中使用的Map,在做一個長時間的操作比如計算之前,先在Map中查詢一下計算的結果是否存在,如果不存在的話再執行計算操作。

我們定義了一個代表計算的接口:

public interface Calculator<A, V> { V calculate(A arg) throws InterruptedException;}

該接口定義了一個calculate方法,接收一個參數,并且返回計算的結果。

我們要定義的緩存就是這個Calculator具體實現的一個封裝。

我們看下用HashMap怎么實現:

public class MemoizedCalculator1<A, V> implements Calculator<A, V> { private final Map<A, V> cache= new HashMap<A, V>(); private final Calculator<A, V> calculator; public MemoizedCalculator1(Calculator<A, V> calculator){ this.calculator=calculator; } @Override public synchronized V calculate(A arg) throws InterruptedException { V result= cache.get(arg); if( result ==null ){ result= calculator.calculate(arg); cache.put(arg, result); } return result; }}

MemoizedCalculator1封裝了Calculator,在調用calculate方法中,實際上調用了封裝的Calculator的calculate方法。

因為HashMap不是線程安全的,所以這里我們使用了synchronized關鍵字,從而保證一次只有一個線程能夠訪問calculate方法。

雖然這樣的設計能夠保證程序的正確執行,但是每次只允許一個線程執行calculate操作,其他調用calculate方法的線程將會被阻塞,在多線程的執行環境中這會嚴重影響速度。從而導致使用緩存可能比不使用緩存需要的時間更長。

使用ConcurrentHashMap

因為HashMap不是線程安全的,那么我們可以嘗試使用線程安全的ConcurrentHashMap來替代HashMap。如下所示:

public class MemoizedCalculator2<A, V> implements Calculator<A, V> { private final Map<A, V> cache= new ConcurrentHashMap<>(); private final Calculator<A, V> calculator; public MemoizedCalculator2(Calculator<A, V> calculator){ this.calculator=calculator; } @Override public V calculate(A arg) throws InterruptedException { V result= cache.get(arg); if( result ==null ){ result= calculator.calculate(arg); cache.put(arg, result); } return result; }}

上面的例子中雖然解決了之前的線程等待的問題,但是當有兩個線程同時在進行同一個計算的時候,仍然不能保證緩存重用,這時候兩個線程都會分別調用計算方法,從而導致重復計算。

我們希望的是如果一個線程正在做計算,其他的線程只需要等待這個線程的執行結果即可。很自然的,我們想到了之前講到的FutureTask。FutureTask表示一個計算過程,我們可以通過調用FutureTask的get方法來獲取執行的結果,如果該執行正在進行中,則會等待。

下面我們使用FutureTask來進行改寫。

FutureTask

@Slf4jpublic class MemoizedCalculator3<A, V> implements Calculator<A, V> { private final Map<A, Future<V>> cache= new ConcurrentHashMap<>(); private final Calculator<A, V> calculator; public MemoizedCalculator3(Calculator<A, V> calculator){ this.calculator=calculator; } @Override public V calculate(A arg) throws InterruptedException { Future<V> future= cache.get(arg); V result=null; if( future ==null ){ Callable<V> callable= new Callable<V>() {@Overridepublic V call() throws Exception { return calculator.calculate(arg);} }; FutureTask<V> futureTask= new FutureTask<>(callable); future= futureTask; cache.put(arg, futureTask); futureTask.run(); } try { result= future.get(); } catch (ExecutionException e) { log.error(e.getMessage(),e); } return result; }}

上面的例子,我們用FutureTask來封裝計算,并且將FutureTask作為Map的value。

上面的例子已經體現了很好的并發性能。但是因為if語句是非原子性的,所以對這一種先檢查后執行的操作,仍然可能存在同一時間調用的情況。

這個時候,我們可以借助于ConcurrentHashMap的原子性操作putIfAbsent來重寫上面的類:

@Slf4jpublic class MemoizedCalculator4<A, V> implements Calculator<A, V> { private final Map<A, Future<V>> cache= new ConcurrentHashMap<>(); private final Calculator<A, V> calculator; public MemoizedCalculator4(Calculator<A, V> calculator){ this.calculator=calculator; } @Override public V calculate(A arg) throws InterruptedException { while (true) { Future<V> future = cache.get(arg); V result = null; if (future == null) {Callable<V> callable = new Callable<V>() { @Override public V call() throws Exception { return calculator.calculate(arg); }};FutureTask<V> futureTask = new FutureTask<>(callable);future = cache.putIfAbsent(arg, futureTask);if (future == null) { future = futureTask; futureTask.run();}try { result = future.get();} catch (CancellationException e) { log.error(e.getMessage(), e); cache.remove(arg, future);} catch (ExecutionException e) { log.error(e.getMessage(), e);}return result; } } }}

上面使用了一個while循環,來判斷從cache中獲取的值是否存在,如果不存在則調用計算方法。

上面我們還要考慮一個緩存污染的問題,因為我們修改了緩存的結果,如果在計算的時候,計算被取消或者失敗,我們需要從緩存中將FutureTask移除。

本文的例子可以參考https://github.com/ddean2009/learn-java-concurrency/tree/master/MemoizedCalculate

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持好吧啦網。

標簽: Java
相關文章:
主站蜘蛛池模板: 中国产业发展研究网 - 提供行业研究报告 可行性研究报告 投资咨询 市场调研服务 | SOUNDWELL 编码器|电位器|旋转编码器|可调电位器|编码开关厂家-广东升威电子制品有限公司 | 冷却塔减速机器_冷却塔皮带箱维修厂家_凉水塔风机电机更换-广东康明冷却塔厂家 | 云南成人高考_云南成考网| 万师讲师网-优质讲师培训师供应商,讲师认证,找讲师来万师 | 考勤系统_人事考勤管理系统_本地部署BS考勤系统_考勤软件_天时考勤管理专家 | 橡胶粉碎机_橡胶磨粉机_轮胎粉碎机_轮胎磨粉机-河南鼎聚重工机械制造有限公司 | 珠光砂保温板-一体化保温板-有釉面发泡陶瓷保温板-杭州一体化建筑材料 | 机床导轨_导轨板_滚轮导轨-上海旻佑精密机械有限公司 | 聚氨酯保温钢管_聚氨酯直埋保温管道_聚氨酯发泡保温管厂家-沧州万荣防腐保温管道有限公司 | 深圳APP开发公司_软件APP定制开发/外包制作-红匣子科技 | 服务器之家 - 专注于服务器技术及软件下载分享 | 滚珠丝杆升降机_螺旋升降机_丝杠升降机-德迈传动 | 橡胶电子拉力机-塑料-微电脑电子拉力试验机厂家-江苏天源 | 青州开防盗门锁-配汽车芯片钥匙-保险箱钥匙-吉祥修锁店 | 商用绞肉机-熟肉切片机-冻肉切丁机-猪肉开条机 - 广州市正盈机械设备有限公司 | 手机游戏_热门软件app下载_好玩的安卓游戏下载基地-吾爱下载站 | 手术室净化装修-手术室净化工程公司-华锐手术室净化厂家 | 企业彩铃制作_移动、联通、电信集团彩铃上传开通_彩铃定制_商务彩铃管理平台-集团彩铃网 | 广州物流公司_广州货运公司_广州回程车运输 - 万信物流 | 送料机_高速冲床送料机_NC伺服滚轮送料机厂家-东莞市久谐自动化设备有限公司 | 招商帮-一站式网络营销服务|互联网整合营销|网络推广代运营|信息流推广|招商帮企业招商好帮手|搜索营销推广|短视视频营销推广 | 合景一建-无尘车间设计施工_食品医药洁净车间工程装修总承包公司 | 防渗膜厂家|养殖防渗膜|水产养殖防渗膜-泰安佳路通工程材料有限公司 | 环氧乙烷灭菌器_压力蒸汽灭菌器_低温等离子过氧化氢灭菌器 _低温蒸汽甲醛灭菌器_清洗工作站_医用干燥柜_灭菌耗材-环氧乙烷灭菌器_脉动真空压力蒸汽灭菌器_低温等离子灭菌设备_河南省三强医疗器械有限责任公司 | 工业洗衣机_工业洗涤设备_上海力净工业洗衣机厂家-洗涤设备首页 bkzzy在职研究生网 - 在职研究生招生信息咨询平台 | 安规_综合测试仪,电器安全性能综合测试仪,低压母线槽安规综合测试仪-青岛合众电子有限公司 | 雄松华章(广州华章MBA)官网-专注MBA/MPA/MPAcc/MEM辅导培训 | 首页|光催化反应器_平行反应仪_光化学反应仪-北京普林塞斯科技有限公司 | 薪动-人力资源公司-灵活用工薪资代发-费用结算-残保金优化-北京秒付科技有限公司 | 章丘丰源机械有限公司 - 三叶罗茨风机,罗茨鼓风机,罗茨风机 | 外贸网站建设-外贸网站设计制作开发公司-外贸独立站建设【企术】 | 威海防火彩钢板,威海岩棉复合板,威海彩钢瓦-文登区九龙岩棉复合板厂 | 汽车整车综合环境舱_军标砂尘_盐雾试验室试验箱-无锡苏南试验设备有限公司 | 煤棒机_增碳剂颗粒机_活性炭颗粒机_木炭粉成型机-巩义市老城振华机械厂 | 长沙发电机-湖南发电机-柴油发电机供应厂家-长沙明邦智能科技 | 「安徽双凯」自动售货机-无人售货机-成人用品-自动饮料食品零食售货机 | 铜镍-康铜-锰铜-电阻合金-NC003 - 杭州兴宇合金有限公司 | 航拍_专业的无人机航拍摄影门户社区网站_航拍网 | 塑胶跑道施工-硅pu篮球场施工-塑胶网球场建造-丙烯酸球场材料厂家-奥茵 | 英语词典_成语词典_日语词典_法语词典_在线词典网 |