java高並發系列 – 第27天:實戰篇,介面性能成倍提升,讓同事刮目相看,現學現用

  • 2019 年 10 月 3 日
  • 筆記

這是java高並發系列第27篇文章。

開發環境:jdk1.8。

案例講解

電商app都有用過吧,商品詳情頁,需要給他們提供一個介面獲取商品相關資訊:

  1. 商品基本資訊(名稱、價格、庫存、會員價格等)
  2. 商品圖片列表
  3. 商品描述資訊(描述資訊一般是由富文本編輯的大文本資訊)

資料庫中我們用了3張表存儲上面的資訊:

  1. 商品基本資訊表:t_goods(欄位:id【商品id】、名稱、價格、庫存、會員價格等)
  2. 商品圖片資訊表:t_goods_imgs(欄位:id、goods_id【商品id】、圖片路徑),一個商品會有多張圖片
  3. 商品描述資訊表:t_goods_ext(欄位:id,goods_id【商品id】、商品描述資訊【大欄位】)

這需求對於大家來說很簡單吧,偽程式碼如下:

public Map<String,Object> detail(long goodsId){      //創建一個map      //step1:查詢商品基本資訊,放入map      map.put("goodsModel",(select * from t_goods where id = #gooldsId#));      //step2:查詢商品圖片列表,返回一個集合放入map      map.put("goodsImgsModelList",(select * from t_goods_imgs where goods_id = #gooldsId#));      //step3:查詢商品描述資訊,放入map      map.put("goodsExtModel",(select * from t_goods_ext where goods_id = #gooldsId#));      return map;  }

上面這種寫法應該很常見,程式碼很簡單,假設上面每個步驟耗時200ms,此介面總共耗時>=600毫秒,其他還涉及到網路傳輸耗時,估計總共會在700ms左右,此介面有沒有優化的空間,性能能夠提升多少?我們一起來挑戰一下。

在看一下上面的邏輯,整個過程是按順序執行的,實際上3個查詢之間是沒有任何依賴關係,所以說3個查詢可以同時執行,那我們對這3個步驟採用多執行緒並行執行,看一下最後什麼情況,程式碼如下:

package com.itsoku.chat26;    import java.util.Arrays;  import java.util.HashMap;  import java.util.List;  import java.util.Map;  import java.util.concurrent.*;    /**   * 跟著阿里p7學並發,微信公眾號:javacode2018   */  public class Demo1 {        /**       * 獲取商品基本資訊       *       * @param goodsId 商品id       * @return 商品基本資訊       * @throws InterruptedException       */      public String goodsDetailModel(long goodsId) throws InterruptedException {          //模擬耗時,休眠200ms          TimeUnit.MILLISECONDS.sleep(200);          return "商品id:" + goodsId + ",商品基本資訊....";      }        /**       * 獲取商品圖片列表       *       * @param goodsId 商品id       * @return 商品圖片列表       * @throws InterruptedException       */      public List<String> goodsImgsModelList(long goodsId) throws InterruptedException {          //模擬耗時,休眠200ms          TimeUnit.MILLISECONDS.sleep(200);          return Arrays.asList("圖1", "圖2", "圖3");      }        /**       * 獲取商品描述資訊       *       * @param goodsId 商品id       * @return 商品描述資訊       * @throws InterruptedException       */      public String goodsExtModel(long goodsId) throws InterruptedException {          //模擬耗時,休眠200ms          TimeUnit.MILLISECONDS.sleep(200);          return "商品id:" + goodsId + ",商品描述資訊......";      }        //創建個執行緒池      ExecutorService executorService = Executors.newFixedThreadPool(10);        /**       * 獲取商品詳情       *       * @param goodsId 商品id       * @return       * @throws ExecutionException       * @throws InterruptedException       */      public Map<String, Object> goodsDetail(long goodsId) throws ExecutionException, InterruptedException {          Map<String, Object> result = new HashMap<>();            //非同步獲取商品基本資訊          Future<String> gooldsDetailModelFuture = executorService.submit(() -> goodsDetailModel(goodsId));          //非同步獲取商品圖片列表          Future<List<String>> goodsImgsModelListFuture = executorService.submit(() -> goodsImgsModelList(goodsId));          //非同步獲取商品描述資訊          Future<String> goodsExtModelFuture = executorService.submit(() -> goodsExtModel(goodsId));            result.put("gooldsDetailModel", gooldsDetailModelFuture.get());          result.put("goodsImgsModelList", goodsImgsModelListFuture.get());          result.put("goodsExtModel", goodsExtModelFuture.get());          return result;      }        public static void main(String[] args) throws ExecutionException, InterruptedException {          long starTime = System.currentTimeMillis();          Map<String, Object> map = new Demo1().goodsDetail(1L);          System.out.println(map);          System.out.println("耗時(ms):" + (System.currentTimeMillis() - starTime));      }  }

輸出:

{goodsImgsModelList=[圖1, 圖2, 圖3], gooldsDetailModel=商品id:1,商品基本資訊...., goodsExtModel=商品id:1,商品描述資訊......}  耗時(ms):208

可以看出耗時200毫秒左右,性能提升了2倍,假如這個介面中還存在其他無依賴的操作,性能提升將更加顯著,上面使用了執行緒池並行去執行3次查詢的任務,最後通過Future獲取非同步執行結果。

整個優化過程:

  1. 先列出無依賴的一些操作
  2. 將這些操作改為並行的方式

用到的技術有:

  1. 執行緒池相關知識
  2. Executors、Future相關知識

總結

  1. 對於無依賴的操作盡量採用並行方式去執行,可以很好的提升介面的性能
  2. 大家可以在你們的系統中試試這種方法,感受一下效果,會讓你感覺很爽

java高並發系列目錄

  1. 第1天:必須知道的幾個概念
  2. 第2天:並發級別
  3. 第3天:有關並行的兩個重要定律
  4. 第4天:JMM相關的一些概念
  5. 第5天:深入理解進程和執行緒
  6. 第6天:執行緒的基本操作
  7. 第7天:volatile與Java記憶體模型
  8. 第8天:執行緒組
  9. 第9天:用戶執行緒和守護執行緒
  10. 第10天:執行緒安全和synchronized關鍵字
  11. 第11天:執行緒中斷的幾種方式
  12. 第12天JUC:ReentrantLock重入鎖
  13. 第13天:JUC中的Condition對象
  14. 第14天:JUC中的LockSupport工具類,必備技能
  15. 第15天:JUC中的Semaphore(訊號量)
  16. 第16天:JUC中等待多執行緒完成的工具類CountDownLatch,必備技能
  17. 第17天:JUC中的循環柵欄CyclicBarrier的6種使用場景
  18. 第18天:JAVA執行緒池,這一篇就夠了
  19. 第19天:JUC中的Executor框架詳解1
  20. 第20天:JUC中的Executor框架詳解2
  21. 第21天:java中的CAS,你需要知道的東西
  22. 第22天:JUC底層工具類Unsafe,高手必須要了解
  23. 第23天:JUC中原子類,一篇就夠了
  24. 第24天:ThreadLocal、InheritableThreadLocal(通俗易懂)
  25. 第25天:掌握JUC中的阻塞隊列

java高並發系列連載中,總計估計會有四五十篇文章。

阿里p7一起學並發,公眾號:路人甲java,每天獲取最新文章!