利用Java代碼寫一個并行調(diào)用模板
前言:
本文主要介紹內(nèi)容有:
- 一個串行調(diào)用的例子(App首頁信息查詢)
- CompletionService實(shí)現(xiàn)并行調(diào)用
- 抽取通用的并行調(diào)用方法
- 代碼思考以及設(shè)計模式應(yīng)用
- 思考總結(jié)
1. 一個串行調(diào)用的例子
如果讓你設(shè)計一個APP首頁查詢的接口,它需要查用戶信息、需要查banner
信息、需要查標(biāo)簽信息等等。
一般情況,小伙伴會實(shí)現(xiàn)如下:
public AppHeadInfoResponse queryAppHeadInfo(AppInfoReq req) { //查用戶信息 UserInfoParam userInfoParam = buildUserParam(req); UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); //查banner信息 BannerParam bannerParam = buildBannerParam(req); BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); //查標(biāo)簽信息 LabelParam labelParam = buildLabelParam(req); LabelDTO labelDTO = labelService.queryLabelInfo(labelParam); //組裝結(jié)果 return buildResponse(userInfoDTO,bannerDTO,labelDTO); }
這段代碼會有什么問題嘛? 其實(shí)這是一段挺正常的代碼,但是這個方法實(shí)現(xiàn)中,查詢用戶、banner、標(biāo)簽信息,是串行的,如果查詢用戶信息200ms
,查詢banner信息100ms
,查詢標(biāo)簽信息200ms
的話,耗時就是500ms
啦。
其實(shí)為了優(yōu)化性能,我們可以修改為并行調(diào)用的方式,耗時可以降為200ms
,如下圖所示:
2. CompletionService實(shí)現(xiàn)并行調(diào)用
對于上面的例子,如何實(shí)現(xiàn)并行調(diào)用呢?
有小伙伴說,可以使用Future+Callable
實(shí)現(xiàn)多個任務(wù)的并行調(diào)用。但是線程池執(zhí)行批量任務(wù)時,返回值用Future的get()
獲取是阻塞的,如果前一個任務(wù)執(zhí)行比較耗時的話,get()
方法會阻塞,形成排隊等待的情況。
而CompletionService
是對定義ExecutorService
進(jìn)行了包裝,可以一邊生成任務(wù),一邊獲取任務(wù)的返回值。讓這兩件事分開執(zhí)行,任務(wù)之間不會互相阻塞,可以獲取最先完成的任務(wù)結(jié)果。
CompletionService
的實(shí)現(xiàn)原理比較簡單,底層通過FutureTask+阻塞隊列,實(shí)現(xiàn)了任務(wù)先完成的話,可優(yōu)先獲取到。也就是說任務(wù)執(zhí)行結(jié)果按照完成的先后順序來排序,先完成可以優(yōu)化獲取到。內(nèi)部有一個先進(jìn)先出的阻塞隊列,用于保存已經(jīng)執(zhí)行完成的Future,你調(diào)用CompletionService
的poll或take方法即可獲取到一個已經(jīng)執(zhí)行完成的Future,進(jìn)而通過調(diào)用Future接口實(shí)現(xiàn)類的get
方法獲取最終的結(jié)果。
接下來,我們來看下,如何用CompletionService
,實(shí)現(xiàn)并行查詢APP首頁信息哈。
思考步驟如下:
我們先把查詢用戶信息的任務(wù),放到線程池,如下:
ExecutorService executor = Executors.newFixedThreadPool(10); //查詢用戶信息 CompletionService<UserInfoDTO> userDTOCompletionService = new ExecutorCompletionService<UserInfoDTO>(executor); Callable<UserInfoDTO> userInfoDTOCallableTask = () -> { UserInfoParam userInfoParam = buildUserParam(req); return userService.queryUserInfo(userInfoParam); }; userDTOCompletionService.submit(userInfoDTOCallableTask);
- 但是如果想把查詢
banner
信息的任務(wù),也放到這個線程池的話,發(fā)現(xiàn)不好放了,因?yàn)榉祷仡愋筒灰粯?,一個是UserInfoDTO
,另外一個是BannerDTO
。那這時候,我們是不是把泛型聲明為Object即可,因?yàn)樗袑ο蠖际抢^承于Object的?如下:
ExecutorService executor = Executors.newFixedThreadPool(10); //查詢用戶信息 CompletionService<Object> baseDTOCompletionService = new ExecutorCompletionService<Object>(executor); Callable<Object> userInfoDTOCallableTask = () -> { UserInfoParam userInfoParam = buildUserParam(req); return userService.queryUserInfo(userInfoParam); }; //banner信息任務(wù) Callable<Object> bannerDTOCallableTask = () -> { BannerParam bannerParam = buildBannerParam(req); return bannerService.queryBannerInfo(bannerParam); }; //提交用戶信息任務(wù) baseDTOCompletionService.submit(userInfoDTOCallableTask); //提交banner信息任務(wù) baseDTOCompletionService.submit(bannerDTOCallableTask);
- 這里會有個問題,就是獲取返回值的時候,我們不知道哪個
Object
是用戶信息的DTO,哪個是BannerDTO
?怎么辦呢?這時候,我們可以在參數(shù)里面做個擴(kuò)展嘛,即參數(shù)聲明為一個基礎(chǔ)對象BaseRspDTO,再搞個泛型放Object數(shù)據(jù)的,然后基礎(chǔ)對象BaseRspDTO有個區(qū)分是UserDTO還是BannerDTO的唯一標(biāo)記屬性key。代碼如下:
public class BaseRspDTO<T extends Object> { //區(qū)分是DTO返回的唯一標(biāo)記,比如是UserInfoDTO還是BannerDTO private String key; //返回的data private T data; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public T getData() { return data; } public void setData(T data) { this.data = data; } } //并行查詢App首頁信息 public AppHeadInfoResponse parallelQueryAppHeadPageInfo(AppInfoReq req) { long beginTime = System.currentTimeMillis(); System.out.println("開始并行查詢app首頁信息,開始時間:" + beginTime); ExecutorService executor = Executors.newFixedThreadPool(10); CompletionService<BaseRspDTO<Object>> baseDTOCompletionService = new ExecutorCompletionService<BaseRspDTO<Object>>(executor); //查詢用戶信息任務(wù) Callable<BaseRspDTO<Object>> userInfoDTOCallableTask = () -> { UserInfoParam userInfoParam = buildUserParam(req); UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); BaseRspDTO<Object> userBaseRspDTO = new BaseRspDTO<Object>(); userBaseRspDTO.setKey("userInfoDTO"); userBaseRspDTO.setData(userInfoDTO); return userBaseRspDTO; }; //banner信息查詢?nèi)蝿?wù) Callable<BaseRspDTO<Object>> bannerDTOCallableTask = () -> { BannerParam bannerParam = buildBannerParam(req); BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); BaseRspDTO<Object> bannerBaseRspDTO = new BaseRspDTO<Object>(); bannerBaseRspDTO.setKey("bannerDTO"); bannerBaseRspDTO.setData(bannerDTO); return bannerBaseRspDTO; }; //label信息查詢?nèi)蝿?wù) Callable<BaseRspDTO<Object>> labelDTODTOCallableTask = () -> { LabelParam labelParam = buildLabelParam(req); LabelDTO labelDTO = labelService.queryLabelInfo(labelParam); BaseRspDTO<Object> labelBaseRspDTO = new BaseRspDTO<Object>(); labelBaseRspDTO.setKey("labelDTO"); labelBaseRspDTO.setData(labelDTO); return labelBaseRspDTO; }; //提交用戶信息任務(wù) baseDTOCompletionService.submit(userInfoDTOCallableTask); //提交banner信息任務(wù) baseDTOCompletionService.submit(bannerDTOCallableTask); //提交label信息任務(wù) baseDTOCompletionService.submit(labelDTODTOCallableTask); UserInfoDTO userInfoDTO = null; BannerDTO bannerDTO = null; LabelDTO labelDTO = null; try { //因?yàn)樘峤涣?個任務(wù),所以獲取結(jié)果次數(shù)是3 for (int i = 0; i < 3; i++) { Future<BaseRspDTO<Object>> baseRspDTOFuture = baseDTOCompletionService.poll(1, TimeUnit.SECONDS); BaseRspDTO baseRspDTO = baseRspDTOFuture.get(); if ("userInfoDTO".equals(baseRspDTO.getKey())) { userInfoDTO = (UserInfoDTO) baseRspDTO.getData(); } else if ("bannerDTO".equals(baseRspDTO.getKey())) { bannerDTO = (BannerDTO) baseRspDTO.getData(); } else if ("labelDTO".equals(baseRspDTO.getKey())) { labelDTO = (LabelDTO) baseRspDTO.getData(); } } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("結(jié)束并行查詢app首頁信息,總耗時:" + (System.currentTimeMillis() - beginTime)); return buildResponse(userInfoDTO, bannerDTO, labelDTO); }
到這里為止,一個基于CompletionService
實(shí)現(xiàn)并行調(diào)用的例子已經(jīng)實(shí)現(xiàn)啦。是不是很開心,哈哈。
3. 抽取通用的并行調(diào)用方法
我們回過來觀察下第2小節(jié),查詢app首頁信息的demo:CompletionService
實(shí)現(xiàn)了并行調(diào)用。大家有沒有什么其他想法呢?比如,假設(shè)別的業(yè)務(wù)場景,也想通過并行調(diào)用優(yōu)化,那是不是也得搞一套類似第2小節(jié)的代碼。所以,我們是不是可以抽取一個通用的并行方法,讓別的場景也可以用,對吧?這就是后端思維啦!
基于第2小節(jié)的代碼,我們?nèi)绾纬槿⊥ㄓ貌⑿姓{(diào)用方法呢。
首先,這個通用的并行調(diào)用方法,不能跟業(yè)務(wù)相關(guān)的屬性掛鉤,對吧,所以方法的入?yún)?yīng)該有哪些呢?
方法的入?yún)?,可以?code>Callable對吧。因?yàn)椴⑿校隙ㄊ嵌鄠€Callable任務(wù)的。所以,入?yún)?yīng)該是一個
Callable
的數(shù)組。再然后,基于上面的APP首頁查詢的例子,Callable
里面得帶BaseRspDTO
泛型,對吧?因此入?yún)⒕褪?code>List<Callable<BaseRspDTO<Object>>> list。
那并行調(diào)用的出參呢? 你有多個Callable
的任務(wù),是不是得有多個對應(yīng)的返回,因此,你的出參可以是List<BaseRspDTO<Object>>
。我們抽取的通用并行調(diào)用模板,就可以寫成醬紫:
public List<BaseRspDTO<Object>> executeTask(List<Callable<BaseRspDTO<Object>>> taskList) { List<BaseRspDTO<Object>> resultList = new ArrayList<>(); //校驗(yàn)參數(shù) if (taskList == null || taskList.size() == 0) { return resultList; } ExecutorService executor = Executors.newFixedThreadPool(10); CompletionService<BaseRspDTO<Object>> baseDTOCompletionService = new ExecutorCompletionService<BaseRspDTO<Object>>(executor); //提交任務(wù) for (Callable<BaseRspDTO<Object>> task : taskList) { baseDTOCompletionService.submit(task); } try { //遍歷獲取結(jié)果 for (int i = 0; i < taskList.size(); i++) { Future<BaseRspDTO<Object>> baseRspDTOFuture = baseDTOCompletionService.poll(2, TimeUnit.SECONDS); resultList.add(baseRspDTOFuture.get()); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } return resultList; }
既然我們是抽取通用的并行調(diào)用方法,那以上的方法是否還有哪些地方需要改進(jìn)的呢?
- 第一個可以優(yōu)化的地方,就是
executor線程池
,比如有些業(yè)務(wù)場景想用A線程池
,有些業(yè)務(wù)想用B線程池
,那么,這個方法,就不通用啦,對吧。我們可以把線程池以參數(shù)的實(shí)行提供出來,給調(diào)用方自己控制。 - 第二個可以優(yōu)化的地方,就是
CompletionService
的poll
方法獲取時,超時時間是寫死的。因?yàn)椴煌瑯I(yè)務(wù)場景,超時時間可能不一樣。所以,超時時間也是可以以參數(shù)形式放出來,給調(diào)用方自己控制。
我們再次優(yōu)化一下這個通用的并行調(diào)用模板,代碼如下:
public List<BaseRspDTO<Object>> executeTask(List<Callable<BaseRspDTO<Object>>> taskList, long timeOut, ExecutorService executor) { List<BaseRspDTO<Object>> resultList = new ArrayList<>(); //校驗(yàn)參數(shù) if (taskList == null || taskList.size() == 0) { return resultList; } if (executor == null) { return resultList; } if (timeOut <= 0) { return resultList; } //提交任務(wù) CompletionService<BaseRspDTO<Object>> baseDTOCompletionService = new ExecutorCompletionService<BaseRspDTO<Object>>(executor); for (Callable<BaseRspDTO<Object>> task : taskList) { baseDTOCompletionService.submit(task); } try { //遍歷獲取結(jié)果 for (int i = 0; i < taskList.size(); i++) { Future<BaseRspDTO<Object>> baseRspDTOFuture = baseDTOCompletionService.poll(timeOut, TimeUnit.SECONDS); resultList.add(baseRspDTOFuture.get()); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } return resultList; }
以后別的場景也需要用到并行調(diào)用的話,直接調(diào)用你的這個方法即可,是不是有點(diǎn)小小的成就感啦,哈哈。
4. 代碼思考以及設(shè)計模式應(yīng)用
我們把抽取的那個公用的并行調(diào)用方法,應(yīng)用到App首頁信息查詢
的例子,
代碼如下:
public AppHeadInfoResponse parallelQueryAppHeadPageInfo1(AppInfoReq req) { long beginTime = System.currentTimeMillis(); System.out.println("開始并行查詢app首頁信息,開始時間:" + beginTime); //用戶信息查詢?nèi)蝿?wù) Callable<BaseRspDTO<Object>> userInfoDTOCallableTask = () -> { UserInfoParam userInfoParam = buildUserParam(req); UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); BaseRspDTO<Object> userBaseRspDTO = new BaseRspDTO<Object>(); userBaseRspDTO.setKey("userInfoDTO"); userBaseRspDTO.setData(userInfoDTO); return userBaseRspDTO; }; //banner信息查詢?nèi)蝿?wù) Callable<BaseRspDTO<Object>> bannerDTOCallableTask = () -> { BannerParam bannerParam = buildBannerParam(req); BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); BaseRspDTO<Object> bannerBaseRspDTO = new BaseRspDTO<Object>(); bannerBaseRspDTO.setKey("bannerDTO"); bannerBaseRspDTO.setData(bannerDTO); return bannerBaseRspDTO; }; //label信息查詢?nèi)蝿?wù) Callable<BaseRspDTO<Object>> labelDTODTOCallableTask = () -> { LabelParam labelParam = buildLabelParam(req); LabelDTO labelDTO = labelService.queryLabelInfo(labelParam); BaseRspDTO<Object> labelBaseRspDTO = new BaseRspDTO<Object>(); labelBaseRspDTO.setKey("labelDTO"); labelBaseRspDTO.setData(labelDTO); return labelBaseRspDTO; }; List<Callable<BaseRspDTO<Object>>> taskList = new ArrayList<>(); taskList.add(userInfoDTOCallableTask); taskList.add(bannerDTOCallableTask); taskList.add(labelDTODTOCallableTask); ExecutorService executor = Executors.newFixedThreadPool(10); List<BaseRspDTO<Object>> resultList = parallelInvokeCommonService.executeTask(taskList, 3, executor); if (resultList == null || resultList.size() == 0) { return new AppHeadInfoResponse(); } UserInfoDTO userInfoDTO = null; BannerDTO bannerDTO = null; LabelDTO labelDTO = null; //遍歷結(jié)果 for (int i = 0; i < resultList.size(); i++) { BaseRspDTO baseRspDTO = resultList.get(i); if ("userInfoDTO".equals(baseRspDTO.getKey())) { userInfoDTO = (UserInfoDTO) baseRspDTO.getData(); } else if ("bannerDTO".equals(baseRspDTO.getKey())) { bannerDTO = (BannerDTO) baseRspDTO.getData(); } else if ("labelDTO".equals(baseRspDTO.getKey())) { labelDTO = (LabelDTO) baseRspDTO.getData(); } } System.out.println("結(jié)束并行查詢app首頁信息,總耗時:" + (System.currentTimeMillis() - beginTime)); return buildResponse(userInfoDTO, bannerDTO, labelDTO); }
基于以上代碼,小伙伴們,是否還有其他方面的優(yōu)化想法呢? 比如這幾個Callable
查詢?nèi)蝿?wù),我們是不是也可以抽取一下?讓代碼更加簡潔。
二話不說,現(xiàn)在我們直接建一個BaseTaskCommand
類,實(shí)現(xiàn)Callable
接口,把查詢用戶信息、查詢banner信息、label標(biāo)簽信息的查詢?nèi)蝿?wù)放進(jìn)去。
代碼如下:
public class BaseTaskCommand implements Callable<BaseRspDTO<Object>> { private String key; private AppInfoReq req; private IUserService userService; private IBannerService bannerService; private ILabelService labelService; public BaseTaskCommand(String key, AppInfoReq req, IUserService userService, IBannerService bannerService, ILabelService labelService) { this.key = key; this.req = req; this.userService = userService; this.bannerService = bannerService; this.labelService = labelService; } @Override public BaseRspDTO<Object> call() throws Exception { if ("userInfoDTO".equals(key)) { UserInfoParam userInfoParam = buildUserParam(req); UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); BaseRspDTO<Object> userBaseRspDTO = new BaseRspDTO<Object>(); userBaseRspDTO.setKey("userInfoDTO"); userBaseRspDTO.setData(userInfoDTO); return userBaseRspDTO; } else if ("bannerDTO".equals(key)) { BannerParam bannerParam = buildBannerParam(req); BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); BaseRspDTO<Object> bannerBaseRspDTO = new BaseRspDTO<Object>(); bannerBaseRspDTO.setKey("bannerDTO"); bannerBaseRspDTO.setData(bannerDTO); return bannerBaseRspDTO; } else if ("labelDTO".equals(key)) { LabelParam labelParam = buildLabelParam(req); LabelDTO labelDTO = labelService.queryLabelInfo(labelParam); BaseRspDTO<Object> labelBaseRspDTO = new BaseRspDTO<Object>(); labelBaseRspDTO.setKey("labelDTO"); labelBaseRspDTO.setData(labelDTO); return labelBaseRspDTO; } return null; } private UserInfoParam buildUserParam(AppInfoReq req) { return new UserInfoParam(); } private BannerParam buildBannerParam(AppInfoReq req) { return new BannerParam(); } private LabelParam buildLabelParam(AppInfoReq req) { return new LabelParam(); } }
以上這塊代碼,構(gòu)造函數(shù)還是有比較多的參數(shù),并且call()
方法中,有多個if...else...
,如果新增一個分支(比如查詢浮層信息),那又得在call
方法里修改了,并且BaseTaskCommand的構(gòu)造器也要修改了。
大家是否有印象,多程序中出現(xiàn)多個if...else...時,我們就可以考慮使用策略模式+工廠模式優(yōu)化。
我們聲明多個策略實(shí)現(xiàn)類,如下:
public interface IBaseTask { //返回每個策略類的key,如 String getTaskType(); BaseRspDTO<Object> execute(AppInfoReq req); } //用戶信息策略類 @Service public class UserInfoStrategyTask implements IBaseTask { @Autowired private IUserService userService; @Override public String getTaskType() { return "userInfoDTO"; } @Override public BaseRspDTO<Object> execute(AppInfoReq req) { UserInfoParam userInfoParam = userService.buildUserParam(req); UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); BaseRspDTO<Object> userBaseRspDTO = new BaseRspDTO<Object>(); userBaseRspDTO.setKey(getTaskType()); userBaseRspDTO.setData(userBaseRspDTO); return userBaseRspDTO; } } /** * banner信息策略實(shí)現(xiàn)類 **/ @Service public class BannerStrategyTask implements IBaseTask { @Autowired private IBannerService bannerService; @Override public String getTaskType() { return "bannerDTO"; } @Override public BaseRspDTO<Object> execute(AppInfoReq req) { BannerParam bannerParam = bannerService.buildBannerParam(req); BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); BaseRspDTO<Object> bannerBaseRspDTO = new BaseRspDTO<Object>(); bannerBaseRspDTO.setKey(getTaskType()); bannerBaseRspDTO.setData(bannerDTO); return bannerBaseRspDTO; } } ...
然后這幾個策略實(shí)現(xiàn)類,怎么交給spring
管理呢? 我們可以實(shí)現(xiàn)ApplicationContextAware
接口,把策略的實(shí)現(xiàn)類注入到一個map,然后根據(jù)請求方不同的策略請求類型(即DTO的類型),去實(shí)現(xiàn)不同的策略類調(diào)用。其實(shí)這類似于工廠模式的思想。
代碼如下:
/** * 策略工廠類 **/ @Component public class TaskStrategyFactory implements ApplicationContextAware { private Map<String, IBaseTask> map = new ConcurrentHashMap<>(); @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map<String, IBaseTask> tempMap = applicationContext.getBeansOfType(IBaseTask.class); tempMap.values().forEach(iBaseTask -> { map.put(iBaseTask.getTaskType(), iBaseTask); }); } public BaseRspDTO<Object> executeTask(String key, AppInfoReq req) { IBaseTask baseTask = map.get(key); if (baseTask != null) { System.out.println("工廠策略實(shí)現(xiàn)類執(zhí)行"); return baseTask.execute(req); } return null; } }
有了策略工廠類TaskStrategyFactory
,我們再回來優(yōu)化下BaseTaskCommand
類的代碼。它的構(gòu)造器已經(jīng)不需要多個IUserService userService, IBannerService bannerService, ILabelService labelService
啦,只需要策略工廠類TaskStrategyFactory
即可。同時策略也不需要多個if...else...
判斷了,用策略工廠類TaskStrategyFactory
代替即可。
優(yōu)化后的代碼如下:
public class BaseTaskCommand implements Callable<BaseRspDTO<Object>> { private String key; private AppInfoReq req; private TaskStrategyFactory taskStrategyFactory; public BaseTaskCommand(String key, AppInfoReq req, TaskStrategyFactory taskStrategyFactory) { this.key = key; this.req = req; this.taskStrategyFactory = taskStrategyFactory; } @Override public BaseRspDTO<Object> call() throws Exception { return taskStrategyFactory.executeTask(key, req); } }
因此整個app首頁信息并行
查詢,就可以優(yōu)化成這樣啦,如下:
public AppHeadInfoResponse parallelQueryAppHeadPageInfo2(AppInfoReq req) { long beginTime = System.currentTimeMillis(); System.out.println("開始并行查詢app首頁信息(最終版本),開始時間:" + beginTime); List<Callable<BaseRspDTO<Object>>> taskList = new ArrayList<>(); //用戶信息查詢?nèi)蝿?wù) taskList.add(new BaseTaskCommand("userInfoDTO", req, taskStrategyFactory)); //banner查詢?nèi)蝿?wù) taskList.add(new BaseTaskCommand("bannerDTO", req, taskStrategyFactory)); //標(biāo)簽查詢?nèi)蝿?wù) taskList.add(new BaseTaskCommand("labelDTO", req, taskStrategyFactory)); ExecutorService executor = Executors.newFixedThreadPool(10); List<BaseRspDTO<Object>> resultList = parallelInvokeCommonService.executeTask(taskList, 3, executor); if (resultList == null || resultList.size() == 0) { return new AppHeadInfoResponse(); } UserInfoDTO userInfoDTO = null; BannerDTO bannerDTO = null; LabelDTO labelDTO = null; for (BaseRspDTO<Object> baseRspDTO : resultList) { if ("userInfoDTO".equals(baseRspDTO.getKey())) { userInfoDTO = (UserInfoDTO) baseRspDTO.getData(); } else if ("bannerDTO".equals(baseRspDTO.getKey())) { bannerDTO = (BannerDTO) baseRspDTO.getData(); } else if ("labelDTO".equals(baseRspDTO.getKey())) { labelDTO = (LabelDTO) baseRspDTO.getData(); } } System.out.println("結(jié)束并行查詢app首頁信息(最終版本),總耗時:" + (System.currentTimeMillis() - beginTime)); return buildResponse(userInfoDTO, bannerDTO, labelDTO); }
5. 思考總結(jié)
以上代碼整體優(yōu)化下來,已經(jīng)很簡潔啦。那還有沒有別的優(yōu)化思路呢。
其實(shí)還是有的,比如,把唯一標(biāo)記的key
定義為枚舉,而不是寫死的字符串"userInfoDTO"、"bannerDTO","labelDTO"
。還有,除了CompletionService
,有些小伙伴喜歡用CompletableFuture
實(shí)行并行調(diào)用。
本文大家學(xué)到了哪些知識呢?
- 如何優(yōu)化接口性能?某些場景下,可以使用并行調(diào)用代替串行。
- 如何實(shí)現(xiàn)并行調(diào)用呢? 可以使用
CompletionService
。 - 學(xué)到的后端思維是? 日常開發(fā)中,要學(xué)會抽取通用的方法、或者工具。
- 策略模式和工廠模式的應(yīng)用
到此這篇關(guān)于利用Java代碼寫一個并行調(diào)用模板的文章就介紹到這了,更多相關(guān)Java并行調(diào)用模板內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 集合框架之List 的使用(附小游戲練習(xí))
這篇文章主要介紹Java 集合框架中List 的使用,下面文章將圍繞Java 集合框架中List 的使用展開話題,并附上一些小游戲練習(xí),需要的朋友可以參考一下2021-10-10java實(shí)現(xiàn)網(wǎng)上購物車程序
這篇文章主要介紹了java實(shí)現(xiàn)網(wǎng)上購物車程序,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01Java單例模式下的MongoDB數(shù)據(jù)庫操作工具類
這篇文章主要介紹了Java單例模式下的MongoDB數(shù)據(jù)庫操作工具類,結(jié)合實(shí)例形式分析了java基于單例模式下操作MongoDB數(shù)據(jù)庫相關(guān)連接、查詢、插入、刪除等操作封裝技巧,需要的朋友可以參考下2018-01-01springboot實(shí)現(xiàn)多模塊項(xiàng)目添加一新模塊
這篇文章主要介紹了springboot實(shí)現(xiàn)多模塊項(xiàng)目添加一新模塊,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02基于CyclicBarrier和CountDownLatch的使用區(qū)別說明
這篇文章主要介紹了基于CyclicBarrier和CountDownLatch的使用區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09