最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java構(gòu)建高效結(jié)果緩存方法示例

 更新時間:2020年04月24日 11:27:03   作者:flydean程序那些事  
這篇文章主要介紹了Java構(gòu)建高效結(jié)果緩存方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

緩存是現(xiàn)代應(yīng)用服務(wù)器中非常常用的組件。除了第三方緩存以外,我們通常也需要在java中構(gòu)建內(nèi)部使用的緩存。那么怎么才能構(gòu)建一個高效的緩存呢? 本文將會一步步的進行揭秘。

使用HashMap

緩存通常的用法就是構(gòu)建一個內(nèi)存中使用的Map,在做一個長時間的操作比如計算之前,先在Map中查詢一下計算的結(jié)果是否存在,如果不存在的話再執(zhí)行計算操作。

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

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

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

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

我們看下用HashMap怎么實現(xiàn):

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,在調(diào)用calculate方法中,實際上調(diào)用了封裝的Calculator的calculate方法。

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

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

使用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;
  }
}

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

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

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

FutureTask

@Slf4j
public 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>() {
        @Override
        public 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。

上面的例子已經(jīng)體現(xiàn)了很好的并發(fā)性能。但是因為if語句是非原子性的,所以對這一種先檢查后執(zhí)行的操作,仍然可能存在同一時間調(diào)用的情況。

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

@Slf4j
public 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循環(huán),來判斷從cache中獲取的值是否存在,如果不存在則調(diào)用計算方法。

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

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

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Mybatis-Plus設(shè)置全局或者局部ID自增的實現(xiàn)

    Mybatis-Plus設(shè)置全局或者局部ID自增的實現(xiàn)

    在使用Mybatis-Plus新增的時候,我們往往想要id隨著數(shù)據(jù)庫自增,本文主要介紹了Mybatis-Plus設(shè)置全局或者局部ID自增的實現(xiàn),具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01
  • 最全總結(jié)SpringBean的作用域管理

    最全總結(jié)SpringBean的作用域管理

    今天給大家詳細總結(jié)了SpringBean的作用域管理,文中有非常詳細的圖文介紹以及代碼示例,對正在學習java的小伙伴們還很有幫助,需要的朋友可以參考下
    2021-05-05
  • spring boot入門開始你的第一個應(yīng)用

    spring boot入門開始你的第一個應(yīng)用

    這篇文章主要介紹了spring boot入門開始你的第一個應(yīng)用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,,需要的朋友可以參考下
    2019-06-06
  • Java中動態(tài)設(shè)置JVM參數(shù)的方法總結(jié)

    Java中動態(tài)設(shè)置JVM參數(shù)的方法總結(jié)

    通過動態(tài)設(shè)置JVM參數(shù),開發(fā)者可以更有效地管理資源使用和優(yōu)化性能,本文將詳細闡述如何在Java中動態(tài)設(shè)置JVM參數(shù),感興趣的小伙伴可以了解下
    2024-12-12
  • 詳解Java中的sleep()和wait()的區(qū)別

    詳解Java中的sleep()和wait()的區(qū)別

    這篇文章主要介紹了詳解Java中的sleep()和wait()的區(qū)別的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-09-09
  • Java執(zhí)行JavaScript代碼

    Java執(zhí)行JavaScript代碼

    這篇文章主要為大家詳細介紹了Java執(zhí)行JavaScript代碼的具體操作方法,感興趣的小伙伴們可以參考一下
    2016-03-03
  • 一文秒懂Java中的樂觀鎖 VS 悲觀鎖

    一文秒懂Java中的樂觀鎖 VS 悲觀鎖

    這篇文章主要介紹了java樂觀鎖 VS 悲觀鎖的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-05-05
  • 從零開始SSM搭建步驟(圖文)

    從零開始SSM搭建步驟(圖文)

    這篇文章主要介紹了從零開始SSM搭建步驟(圖文),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-04-04
  • Java中HashMap的put過程詳解

    Java中HashMap的put過程詳解

    這篇文章主要介紹了Java中HashMap的put過程詳解,HashMap有4個構(gòu)造器,其他構(gòu)造器如果用戶沒有傳入initialCapacity?和loadFactor這兩個參數(shù),會使用默認值一般如果new?HashMap()不傳值,需要的朋友可以參考下
    2023-07-07
  • java中歸并排序和Master公式詳解

    java中歸并排序和Master公式詳解

    大家好,本篇文章主要講的是java中歸并排序和Master公式詳解,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01

最新評論

莱阳市| 广德县| 巴东县| 商洛市| 莆田市| 富裕县| 绍兴市| 乐平市| 高平市| 富阳市| 时尚| 交口县| 阳城县| 台北县| 田林县| 清丰县| 安阳市| 广西| 万盛区| 长沙县| 新营市| 剑河县| 英山县| 庆元县| 黔南| 新安县| 东丽区| 蕉岭县| 右玉县| 开远市| 城口县| 阿图什市| 罗江县| 奎屯市| 乌兰浩特市| 民县| 河东区| 商河县| 玛沁县| 塘沽区| 大化|