真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java怎么構(gòu)建高效結(jié)果緩存

這篇文章主要講解了Java怎么構(gòu)建高效結(jié)果緩存,內(nèi)容清晰明了,對(duì)此有興趣的小伙伴可以學(xué)習(xí)一下,相信大家閱讀完之后會(huì)有幫助。

北碚網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)自2013年起到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專(zhuān)注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。

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

使用HashMap

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

我們定義了一個(gè)代表計(jì)算的接口:

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

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

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

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

public class MemoizedCalculator1 implements Calculator {

  private final Map cache= new HashMap();
  private final Calculator calculator;
  public MemoizedCalculator1(Calculator 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方法中,實(shí)際上調(diào)用了封裝的Calculator的calculate方法。

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

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

使用ConcurrentHashMap

因?yàn)镠ashMap不是線程安全的,那么我們可以嘗試使用線程安全的ConcurrentHashMap來(lái)替代HashMap。如下所示:

public class MemoizedCalculator2 implements Calculator {

  private final Map cache= new ConcurrentHashMap<>();
  private final Calculator calculator;
  public MemoizedCalculator2(Calculator 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;
  }
}

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

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

下面我們使用FutureTask來(lái)進(jìn)行改寫(xiě)。

FutureTask

@Slf4j
public class MemoizedCalculator3 implements Calculator {

  private final Map> cache= new ConcurrentHashMap<>();
  private final Calculator calculator;

  public MemoizedCalculator3(Calculator calculator){
    this.calculator=calculator;
  }
  @Override
  public V calculate(A arg) throws InterruptedException {
    Future future= cache.get(arg);
    V result=null;
    if( future ==null ){
      Callable callable= new Callable() {
        @Override
        public V call() throws Exception {
          return calculator.calculate(arg);
        }
      };
      FutureTask 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來(lái)封裝計(jì)算,并且將FutureTask作為Map的value。

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

這個(gè)時(shí)候,我們可以借助于ConcurrentHashMap的原子性操作putIfAbsent來(lái)重寫(xiě)上面的類(lèi):

@Slf4j
public class MemoizedCalculator4 implements Calculator {

  private final Map> cache= new ConcurrentHashMap<>();
  private final Calculator calculator;

  public MemoizedCalculator4(Calculator calculator){
    this.calculator=calculator;
  }
  @Override
  public V calculate(A arg) throws InterruptedException {
    while (true) {
      Future future = cache.get(arg);
      V result = null;
      if (future == null) {
        Callable callable = new Callable() {
          @Override
          public V call() throws Exception {
            return calculator.calculate(arg);
          }
        };
        FutureTask 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;
      }
    }
  }
}

上面使用了一個(gè)while循環(huán),來(lái)判斷從cache中獲取的值是否存在,如果不存在則調(diào)用計(jì)算方法。

上面我們還要考慮一個(gè)緩存污染的問(wèn)題,因?yàn)槲覀冃薷牧司彺娴慕Y(jié)果,如果在計(jì)算的時(shí)候,計(jì)算被取消或者失敗,我們需要從緩存中將FutureTask移除。

看完上述內(nèi)容,是不是對(duì)Java怎么構(gòu)建高效結(jié)果緩存有進(jìn)一步的了解,如果還想學(xué)習(xí)更多內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


網(wǎng)站題目:Java怎么構(gòu)建高效結(jié)果緩存
鏈接URL:http://weahome.cn/article/gjsoci.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部