實(shí)踐筆記分享:Java8多線程幾種場景實(shí)現(xiàn)

微信公眾號:[Java技術(shù)干貨]
關(guān)注Java技術(shù)、關(guān)注前端后端全棧技術(shù)。問題或建議,請公眾號留言。

最近在弄一個高并發(fā)項目,經(jīng)過接口壓測后,各項指標(biāo)不禁人意,也一直在搞程序調(diào)優(yōu)(Nginx、Redis、數(shù)據(jù)庫)。

真的是被虐得是體無完膚,發(fā)絲也日漸脆弱。

哎.............還在錘煉中.........

在調(diào)優(yōu)的過程中,我把在程序中遇到多線程優(yōu)化的幾個場景案例記錄分享一下,以供學(xué)習(xí)和交流。

場景一:數(shù)據(jù)拆分多個subList, 分批多線程導(dǎo)入

// map拆分成多個subList
List<Map<String, List<UserParam>>> userParams= MapUtil.mapChunk(userParam, BATCH_COUNT);
AtomicReference<CompletableFuture<Void>> all = new AtomicReference<>();
userParams.stream().forEach(userListMap -> {
       // 每一個subList 創(chuàng)建一個線程處理,以下是無參返回
       CompletableFuture<Void>  cf  = CompletableFuture.runAsync(() -> {
           try {
               // 此處要注意父級線程往子線程的參數(shù)傳遞,不然在子線程中會存在取不到值的情況
               List<UserParam> threadTemp = new ArrayList<>();
               userListMap.entrySet().stream().forEach(s -> {
                   threadTemp.addAll(s.getValue());
               });
               // 保存業(yè)務(wù)數(shù)據(jù)
               saveData(threadTemp);
           } finally {
                        
          }
    });
   all.set(CompletableFuture.allOf(cf));
});
all.get().join();

場景二:數(shù)據(jù)列表查詢(一個方法多線程處理業(yè)務(wù)) 拆分多線程處理

List<CompletableFuture> comList = new ArrayList<>();
// 該計數(shù)器,是為了等待所有線程都執(zhí)行完了,在往后執(zhí)行
CountDownLatch countDownLatch = new CountDownLatch(1); // 1 代表會初始化1個計數(shù),這個是跟隨創(chuàng)建線程數(shù)量保持一致
CompletableFuture<Void>  cf  = CompletableFuture.runAsync(() -> {
  
  // 具體寫業(yè)務(wù)的地方
  ......

}, asyncExecutor); // 采用異步線程交給線程池,避免無限創(chuàng)建線程
// 添加一個異步等待線程去監(jiān)聽cf 是否完成執(zhí)行
CompletableFuture cf2 = cf.whenCompleteAsync((result, error) -> {
    // 如果完成,則把計數(shù)器減1
    countDownLatch.countDown();
});
comlist.add(cf2);

// 最后固定寫法
CompletableFuture<Void> all = CompletableFuture.allOf(comList.toArray(new CompletableFuture[comList.size()]));
all.join();
try {
      countDownLatch.await();
 } catch (InterruptedException e) {
      log.error("線程中斷異常:{}", e);
}

場景三:多線程帶返回值處理

CountDownLatch countDownLatch = new CountDownLatch(count);
List<CompletableFuture> comlist = new ArrayList<>();
List<PdfContent> resultList = new ArrayList<>();
CompletableFuture<PdfContent> futureResult = CompletableFuture.supplyAsync(()->{
   PdfContent result = new PdfContent;

  // 業(yè)務(wù)處理 返回result結(jié)果 
 
  return result;
}, asyncExecutor);

CompletableFuture futureCompleteResult = futureResult.whenCompleteAsync((result, exception) -> {
     resultList.add(result);
     countDownLatch.countDown();
});
comlist.add(futureCompleteResult);

// 最后都一樣的處理
CompletableFuture<Void> all = CompletableFuture.allOf(comlist.toArray(new CompletableFuture[count]));
all.join();

try {
     countDownLatch.await();
      // 對resultList結(jié)果進(jìn)行排序,多線程處理返回結(jié)果是無序的,需要進(jìn)行排序
     Collections.sort(resultList, new Comparator<PdfContent>() {
           @Override
           public int compare(PdfContent o1, PdfContent o2) {
               return o1.getIndex() - o2.getIndex();
           }
     });
} catch (InterruptedException e) {
     e.printStackTrace();
}

上面的是我在項目采用多線程優(yōu)化過程中,一些常用的多線程場景寫法,趕快收藏起來。

注意點(diǎn)

在寫多線程的過程中,需要注意線程間的共享變量傳遞,比如Request對象,SecurityContext上下

文。因?yàn)檫@些對象信息是用ThreadLocal 存儲的,如果里面存在多線程,變量值是不會傳遞,需要

在子線程顯式的賦值對象和移除對象。

總結(jié)

經(jīng)過該項目的歷練,也讓我學(xué)習(xí)到整個項目調(diào)優(yōu)全貌過程。

從壓測工具使用和壓測腳本編寫

再到程序調(diào)優(yōu),中間件(Nginx/Redis/Mysql)調(diào)優(yōu)

監(jiān)控程序進(jìn)行性能分析,找到造成QPS不高的問題點(diǎn)

也算是有個比較全面的項目調(diào)優(yōu)經(jīng)驗(yàn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容