亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

利用Java代碼寫(xiě)一個(gè)并行調(diào)用模板

 更新時(shí)間:2022年05月20日 11:24:11   作者:??撿田螺的小男孩????  
這篇文章主要介紹了利用Java代碼寫(xiě)一個(gè)并行調(diào)用模板,文章基于Java的相關(guān)內(nèi)容展開(kāi)寫(xiě)一個(gè)并行調(diào)用模板的詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

前言:

本文主要介紹內(nèi)容有:

  • 一個(gè)串行調(diào)用的例子(App首頁(yè)信息查詢(xún))
  • CompletionService實(shí)現(xiàn)并行調(diào)用
  • 抽取通用的并行調(diào)用方法
  • 代碼思考以及設(shè)計(jì)模式應(yīng)用
  • 思考總結(jié)

1. 一個(gè)串行調(diào)用的例子

如果讓你設(shè)計(jì)一個(gè)APP首頁(yè)查詢(xún)的接口,它需要查用戶(hù)信息、需要查banner信息、需要查標(biāo)簽信息等等。

般情況,小伙伴會(huì)實(shí)現(xiàn)如下:

public AppHeadInfoResponse queryAppHeadInfo(AppInfoReq req) {
    //查用戶(hù)信息
    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);
}

這段代碼會(huì)有什么問(wèn)題嘛? 其實(shí)這是一段挺正常的代碼,但是這個(gè)方法實(shí)現(xiàn)中,查詢(xún)用戶(hù)、banner、標(biāo)簽信息,是串行的,如果查詢(xún)用戶(hù)信息200ms,查詢(xún)banner信息100ms,查詢(xún)標(biāo)簽信息200ms的話,耗時(shí)就是500ms啦。

其實(shí)為了優(yōu)化性能,我們可以修改為并行調(diào)用的方式,耗時(shí)可以降為200ms,如下圖所示:

2. CompletionService實(shí)現(xiàn)并行調(diào)用

對(duì)于上面的例子,如何實(shí)現(xiàn)并行調(diào)用呢?

有小伙伴說(shuō),可以使用Future+Callable實(shí)現(xiàn)多個(gè)任務(wù)的并行調(diào)用。但是線程池執(zhí)行批量任務(wù)時(shí),返回值用Future的get()獲取是阻塞的,如果前一個(gè)任務(wù)執(zhí)行比較耗時(shí)的話,get()方法會(huì)阻塞,形成排隊(duì)等待的情況。

CompletionService是對(duì)定義ExecutorService進(jìn)行了包裝,可以一邊生成任務(wù),一邊獲取任務(wù)的返回值。讓這兩件事分開(kāi)執(zhí)行,任務(wù)之間不會(huì)互相阻塞,可以獲取最先完成的任務(wù)結(jié)果。

CompletionService的實(shí)現(xiàn)原理比較簡(jiǎn)單,底層通過(guò)FutureTask+阻塞隊(duì)列,實(shí)現(xiàn)了任務(wù)先完成的話,可優(yōu)先獲取到。也就是說(shuō)任務(wù)執(zhí)行結(jié)果按照完成的先后順序來(lái)排序,先完成可以?xún)?yōu)化獲取到。內(nèi)部有一個(gè)先進(jìn)先出的阻塞隊(duì)列,用于保存已經(jīng)執(zhí)行完成的Future,你調(diào)用CompletionService的poll或take方法即可獲取到一個(gè)已經(jīng)執(zhí)行完成的Future,進(jìn)而通過(guò)調(diào)用Future接口實(shí)現(xiàn)類(lèi)的get方法獲取最終的結(jié)果。

接下來(lái),我們來(lái)看下,如何用CompletionService,實(shí)現(xiàn)并行查詢(xún)APP首頁(yè)信息哈。

思考步驟如下:

我們先把查詢(xún)用戶(hù)信息的任務(wù),放到線程池,如下:

ExecutorService executor = Executors.newFixedThreadPool(10);
//查詢(xún)用戶(hù)信息
CompletionService<UserInfoDTO> userDTOCompletionService = new ExecutorCompletionService<UserInfoDTO>(executor);
Callable<UserInfoDTO> userInfoDTOCallableTask = () -> {
      UserInfoParam userInfoParam = buildUserParam(req);
      return userService.queryUserInfo(userInfoParam);
  };
userDTOCompletionService.submit(userInfoDTOCallableTask);
  • 但是如果想把查詢(xún)banner信息的任務(wù),也放到這個(gè)線程池的話,發(fā)現(xiàn)不好放了,因?yàn)榉祷仡?lèi)型不一樣,一個(gè)是UserInfoDTO,另外一個(gè)是BannerDTO。那這時(shí)候,我們是不是把泛型聲明為Object即可,因?yàn)樗袑?duì)象都是繼承于Object的?如下:
ExecutorService executor = Executors.newFixedThreadPool(10);
//查詢(xún)用戶(hù)信息
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);
};
//提交用戶(hù)信息任務(wù)
baseDTOCompletionService.submit(userInfoDTOCallableTask);
//提交banner信息任務(wù)
baseDTOCompletionService.submit(bannerDTOCallableTask);
  • 這里會(huì)有個(gè)問(wèn)題,就是獲取返回值的時(shí)候,我們不知道哪個(gè)Object是用戶(hù)信息的DTO,哪個(gè)是BannerDTO?怎么辦呢?這時(shí)候,我們可以在參數(shù)里面做個(gè)擴(kuò)展嘛,即參數(shù)聲明為一個(gè)基礎(chǔ)對(duì)象BaseRspDTO,再搞個(gè)泛型放Object數(shù)據(jù)的,然后基礎(chǔ)對(duì)象BaseRspDTO有個(gè)區(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;
    }
}
//并行查詢(xún)App首頁(yè)信息
public AppHeadInfoResponse parallelQueryAppHeadPageInfo(AppInfoReq req) {
    long beginTime = System.currentTimeMillis();
    System.out.println("開(kāi)始并行查詢(xún)app首頁(yè)信息,開(kāi)始時(shí)間:" + beginTime);
    ExecutorService executor = Executors.newFixedThreadPool(10);
    CompletionService<BaseRspDTO<Object>> baseDTOCompletionService = new ExecutorCompletionService<BaseRspDTO<Object>>(executor);
    //查詢(xún)用戶(hù)信息任務(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信息查詢(xún)?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信息查詢(xún)?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;
    };
    //提交用戶(hù)信息任務(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)樘峤涣?個(gè)任務(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é)束并行查詢(xún)app首頁(yè)信息,總耗時(shí):" + (System.currentTimeMillis() - beginTime));
    return buildResponse(userInfoDTO, bannerDTO, labelDTO);
}

到這里為止,一個(gè)基于CompletionService實(shí)現(xiàn)并行調(diào)用的例子已經(jīng)實(shí)現(xiàn)啦。是不是很開(kāi)心,哈哈。

3. 抽取通用的并行調(diào)用方法

我們回過(guò)來(lái)觀察下第2小節(jié),查詢(xún)app首頁(yè)信息的demo:CompletionService實(shí)現(xiàn)了并行調(diào)用。大家有沒(méi)有什么其他想法呢?比如,假設(shè)別的業(yè)務(wù)場(chǎng)景,也想通過(guò)并行調(diào)用優(yōu)化,那是不是也得搞一套類(lèi)似第2小節(jié)的代碼。所以,我們是不是可以抽取一個(gè)通用的并行方法,讓別的場(chǎng)景也可以用,對(duì)吧?這就是后端思維啦

基于第2小節(jié)的代碼,我們?nèi)绾纬槿⊥ㄓ貌⑿姓{(diào)用方法呢。

首先,這個(gè)通用的并行調(diào)用方法,不能跟業(yè)務(wù)相關(guān)的屬性掛鉤,對(duì)吧,所以方法的入?yún)?yīng)該有哪些呢?

方法的入?yún)?,可以?code>Callable對(duì)吧。因?yàn)椴⑿校隙ㄊ嵌鄠€(gè)Callable任務(wù)的。所以,入?yún)?yīng)該是一個(gè)Callable的數(shù)組。再然后,基于上面的APP首頁(yè)查詢(xún)的例子,Callable里面得帶BaseRspDTO泛型,對(duì)吧?因此入?yún)⒕褪?code>List<Callable<BaseRspDTO<Object>>> list。

那并行調(diào)用的出參呢? 你有多個(gè)Callable的任務(wù),是不是得有多個(gè)對(duì)應(yīng)的返回,因此,你的出參可以是List<BaseRspDTO<Object>>。我們抽取的通用并行調(diào)用模板,就可以寫(xiě)成醬紫:

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)的呢?

  • 第一個(gè)可以?xún)?yōu)化的地方,就是executor線程池,比如有些業(yè)務(wù)場(chǎng)景想用A線程池,有些業(yè)務(wù)想用B線程池,那么,這個(gè)方法,就不通用啦,對(duì)吧。我們可以把線程池以參數(shù)的實(shí)行提供出來(lái),給調(diào)用方自己控制。
  • 第二個(gè)可以?xún)?yōu)化的地方,就是CompletionServicepoll方法獲取時(shí),超時(shí)時(shí)間是寫(xiě)死的。因?yàn)椴煌瑯I(yè)務(wù)場(chǎng)景,超時(shí)時(shí)間可能不一樣。所以,超時(shí)時(shí)間也是可以以參數(shù)形式放出來(lái),給調(diào)用方自己控制。

我們?cè)俅蝺?yōu)化一下這個(gè)通用的并行調(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;
}

以后別的場(chǎng)景也需要用到并行調(diào)用的話,直接調(diào)用你的這個(gè)方法即可,是不是有點(diǎn)小小的成就感啦,哈哈。

4. 代碼思考以及設(shè)計(jì)模式應(yīng)用

我們把抽取的那個(gè)公用的并行調(diào)用方法,應(yīng)用到App首頁(yè)信息查詢(xún)的例子,

代碼如下:

public AppHeadInfoResponse parallelQueryAppHeadPageInfo1(AppInfoReq req) {
        long beginTime = System.currentTimeMillis();
        System.out.println("開(kāi)始并行查詢(xún)app首頁(yè)信息,開(kāi)始時(shí)間:" + beginTime);
        //用戶(hù)信息查詢(xún)?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信息查詢(xún)?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信息查詢(xún)?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é)束并行查詢(xún)app首頁(yè)信息,總耗時(shí):" + (System.currentTimeMillis() - beginTime));
        return buildResponse(userInfoDTO, bannerDTO, labelDTO);
    }

基于以上代碼,小伙伴們,是否還有其他方面的優(yōu)化想法呢? 比如這幾個(gè)Callable查詢(xún)?nèi)蝿?wù),我們是不是也可以抽取一下?讓代碼更加簡(jiǎn)潔。

二話不說(shuō),現(xiàn)在我們直接建一個(gè)BaseTaskCommand類(lèi),實(shí)現(xiàn)Callable接口,把查詢(xún)用戶(hù)信息、查詢(xún)banner信息、label標(biāo)簽信息的查詢(xún)?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()方法中,有多個(gè)if...else...,如果新增一個(gè)分支(比如查詢(xún)浮層信息),那又得在call方法里修改了,并且BaseTaskCommand的構(gòu)造器也要修改了。

大家是否有印象,多程序中出現(xiàn)多個(gè)if...else...時(shí),我們就可以考慮使用策略模式+工廠模式優(yōu)化。

我們聲明多個(gè)策略實(shí)現(xiàn)類(lèi),如下:

public interface IBaseTask {
    //返回每個(gè)策略類(lèi)的key,如
    String getTaskType();
    BaseRspDTO<Object> execute(AppInfoReq req);
}
//用戶(hù)信息策略類(lèi)
@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)類(lèi)
  **/
@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;
    }
}
...

然后這幾個(gè)策略實(shí)現(xiàn)類(lèi),怎么交給spring管理呢? 我們可以實(shí)現(xiàn)ApplicationContextAware接口,把策略的實(shí)現(xiàn)類(lèi)注入到一個(gè)map,然后根據(jù)請(qǐng)求方不同的策略請(qǐng)求類(lèi)型(即DTO的類(lèi)型),去實(shí)現(xiàn)不同的策略類(lèi)調(diào)用。其實(shí)這類(lèi)似于工廠模式的思想。

代碼如下:

/**
  * 策略工廠類(lèi)
  **/
@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)類(lèi)執(zhí)行");
            return baseTask.execute(req);
        }
        return null;
    }
}

有了策略工廠類(lèi)TaskStrategyFactory,我們?cè)倩貋?lái)優(yōu)化下BaseTaskCommand類(lèi)的代碼。它的構(gòu)造器已經(jīng)不需要多個(gè)IUserService userService, IBannerService bannerService, ILabelService labelService啦,只需要策略工廠類(lèi)TaskStrategyFactory即可。同時(shí)策略也不需要多個(gè)if...else...判斷了,用策略工廠類(lèi)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);
    }
}  

因此整個(gè)app首頁(yè)信息并行查詢(xún),就可以?xún)?yōu)化成這樣啦,如下:

public AppHeadInfoResponse parallelQueryAppHeadPageInfo2(AppInfoReq req) {
    long beginTime = System.currentTimeMillis();
    System.out.println("開(kāi)始并行查詢(xún)app首頁(yè)信息(最終版本),開(kāi)始時(shí)間:" + beginTime);
    List<Callable<BaseRspDTO<Object>>> taskList = new ArrayList<>();
    //用戶(hù)信息查詢(xún)?nèi)蝿?wù)
    taskList.add(new BaseTaskCommand("userInfoDTO", req, taskStrategyFactory));
    //banner查詢(xún)?nèi)蝿?wù)
    taskList.add(new BaseTaskCommand("bannerDTO", req, taskStrategyFactory));
    //標(biāo)簽查詢(xún)?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é)束并行查詢(xún)app首頁(yè)信息(最終版本),總耗時(shí):" + (System.currentTimeMillis() - beginTime));
    return buildResponse(userInfoDTO, bannerDTO, labelDTO);
  }

5. 思考總結(jié)

以上代碼整體優(yōu)化下來(lái),已經(jīng)很簡(jiǎn)潔啦。那還有沒(méi)有別的優(yōu)化思路呢。

其實(shí)還是有的,比如,把唯一標(biāo)記的key定義為枚舉,而不是寫(xiě)死的字符串"userInfoDTO"、"bannerDTO","labelDTO"。還有,除了CompletionService,有些小伙伴喜歡用CompletableFuture實(shí)行并行調(diào)用。

本文大家學(xué)到了哪些知識(shí)呢?

  • 如何優(yōu)化接口性能?某些場(chǎng)景下,可以使用并行調(diào)用代替串行。
  • 如何實(shí)現(xiàn)并行調(diào)用呢? 可以使用CompletionService
  • 學(xué)到的后端思維是? 日常開(kāi)發(fā)中,要學(xué)會(huì)抽取通用的方法、或者工具。
  • 策略模式和工廠模式的應(yīng)用

到此這篇關(guān)于利用Java代碼寫(xiě)一個(gè)并行調(diào)用模板的文章就介紹到這了,更多相關(guān)Java并行調(diào)用模板內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 集合框架之List 的使用(附小游戲練習(xí))

    Java 集合框架之List 的使用(附小游戲練習(xí))

    這篇文章主要介紹Java 集合框架中List 的使用,下面文章將圍繞Java 集合框架中List 的使用展開(kāi)話題,并附上一些小游戲練習(xí),需要的朋友可以參考一下
    2021-10-10
  • Java經(jīng)典面試題之NIO多路復(fù)用

    Java經(jīng)典面試題之NIO多路復(fù)用

    JAVA?NIO?的多路復(fù)用是面試中經(jīng)常被問(wèn)的問(wèn)題,今天我們徹底搞明白究竟是怎么回事,文中的示例代碼講解詳細(xì),希望對(duì)大家學(xué)習(xí)Java有所幫助
    2023-06-06
  • java實(shí)現(xiàn)網(wǎng)上購(gòu)物車(chē)程序

    java實(shí)現(xiàn)網(wǎng)上購(gòu)物車(chē)程序

    這篇文章主要介紹了java實(shí)現(xiàn)網(wǎng)上購(gòu)物車(chē)程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Java單例模式下的MongoDB數(shù)據(jù)庫(kù)操作工具類(lèi)

    Java單例模式下的MongoDB數(shù)據(jù)庫(kù)操作工具類(lèi)

    這篇文章主要介紹了Java單例模式下的MongoDB數(shù)據(jù)庫(kù)操作工具類(lèi),結(jié)合實(shí)例形式分析了java基于單例模式下操作MongoDB數(shù)據(jù)庫(kù)相關(guān)連接、查詢(xún)、插入、刪除等操作封裝技巧,需要的朋友可以參考下
    2018-01-01
  • Java調(diào)用.dll文件的方法

    Java調(diào)用.dll文件的方法

    因?yàn)轫?xiàng)目的需求,要在JAVA項(xiàng)目中調(diào)用Windows的Dll(動(dòng)態(tài)鏈接庫(kù))文件,之前用Jni調(diào)用過(guò)C寫(xiě)的Dll文件,比較麻煩,這里不多說(shuō),網(wǎng)上也有很多這方面的文檔。在網(wǎng)上找到一個(gè)開(kāi)源的組件JNative,使用后感覺(jué)比較方便
    2013-04-04
  • Java的原子類(lèi)無(wú)鎖并發(fā)利器詳解

    Java的原子類(lèi)無(wú)鎖并發(fā)利器詳解

    這篇文章主要介紹了Java的原子類(lèi)無(wú)鎖并發(fā)利器詳解,原子類(lèi)同樣能夠解決互斥性問(wèn)題、原子性問(wèn)題除此之外,因?yàn)樵宇?lèi)是無(wú)鎖操作,沒(méi)有用互斥鎖解決帶來(lái)的加鎖解決性能消耗,這種絕佳方案是怎么做到的呢,需要的朋友可以參考下
    2023-12-12
  • java設(shè)計(jì)模式--建造者模式詳解

    java設(shè)計(jì)模式--建造者模式詳解

    這篇文章主要介紹了Java設(shè)計(jì)模式之建造者模式,結(jié)合具體實(shí)例形式分析了建造者模式的概念、原理、實(shí)現(xiàn)方法與相關(guān)使用注意事項(xiàng),需要的朋友可以參考下
    2021-07-07
  • java基礎(chǔ)--JDK SPI概述

    java基礎(chǔ)--JDK SPI概述

    SPI是一種服務(wù)發(fā)現(xiàn)機(jī)制,本文就SPI做了詳細(xì)概述,具有很好的參考價(jià)值,希望對(duì)小伙伴們有所幫助,感興趣的朋友一起來(lái)參考參考吧
    2021-08-08
  • springboot實(shí)現(xiàn)多模塊項(xiàng)目添加一新模塊

    springboot實(shí)現(xiàn)多模塊項(xiàng)目添加一新模塊

    這篇文章主要介紹了springboot實(shí)現(xiàn)多模塊項(xiàng)目添加一新模塊,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 基于CyclicBarrier和CountDownLatch的使用區(qū)別說(shuō)明

    基于CyclicBarrier和CountDownLatch的使用區(qū)別說(shuō)明

    這篇文章主要介紹了基于CyclicBarrier和CountDownLatch的使用區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09

最新評(píng)論