目录
背景
今天学习了线性执行和并行执行的区别。
如果要同时去查询不同库里的不同信息,正常的小伙伴都是这么写:
public AppHeadInfoResponse queryAppHeadInfo(AppInfoReq req) {
//查
UserInfoParam userInfoParam = buildUserParam(req);
UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam);
//查
BannerParam bannerParam = buildBannerParam(req);
BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam);
//查
LabelParam labelParam = buildLabelParam(req);
LabelDTO labelDTO = labelService.queryLabelInfo(labelParam);
//组
return buildResponse(userInfoDTO,bannerDTO,labelDTO);
}
非常合理且没有毛病;但是本着后端要有优化思维,我今天学习了并行模式。
为什么要这样的?
比如第一个查询100s,第二个90s,第三个80s,这样下去就是270s,如果并行起来100s是不是就足够了,哪怕不能全并行,那也一定是更快的。
实现
CompletionService
是对定义ExecutorService
进行了包装,可以一边生成任务,一边获取任务的返回值。让这两件事分开执行,任务之间不会互相阻塞,可以获取最先完成的任务结果。
CompletionService
的实现原理比较简单,底层通过FutureTask+阻塞队列,实现了任务先完成的话,可优先获取到。也就是说任务执行结果按照完成的先后顺序来排序,先完成可以优先获取到。内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,你调用CompletionService
的poll或take方法即可获取到一个已经执行完成的Future,进而通过调用Future接口实现类的get
方法获取最终的结果。
根据这个东西,手撸一个用例:(执行代码块使用计算斐波那契函数)
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) {
test1();
System.out.println("------------------------------");
test2();
}
public static void test2() {
long beginTime = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(10);
CompletionService<Object> completionService = new ExecutorCompletionService<>(executor);
Callable<Object> taskList = () -> fibonacci(40);
Callable<Object> taskList1 = () -> fibonacci(40);
Callable<Object> taskList2 = () -> fibonacci(40);
completionService.submit(taskList);
completionService.submit(taskList1);
completionService.submit(taskList2);
try {
//因为提交了3个任务,所以获取结果次数是3
for (int i = 0; i < 3; i++) {
Future<Object> baseRspDTOFuture = completionService.poll(1, TimeUnit.SECONDS);
Object o = baseRspDTOFuture.get();
System.out.println("o");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("------------------------");
System.out.println("总时长:" + (System.currentTimeMillis() - beginTime));
System.exit(0);
}
public static void test1() {
long beginTime = System.currentTimeMillis();
fibonacci(40);
long secondTime = System.currentTimeMillis();
System.out.println("1使用时间:" + ( secondTime - beginTime ));
fibonacci(40);
long thirdTime = System.currentTimeMillis();
System.out.println("2使用时间:" + (thirdTime-secondTime));
fibonacci(40);
System.out.println("3使用时间:" + (System.currentTimeMillis() - thirdTime) + "总时长:" + (System.currentTimeMillis() - beginTime));
}
public static int fibonacci(int n) {
if (n <= 1) {
return n;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
}
贴出几个运行结果:
1使用时间:578
2使用时间:577
3使用时间:486总时长:1641
------------------------------
总时长:607
1使用时间:615
2使用时间:518
3使用时间:377总时长:1510
------------------------------
总时长:574
效果显著。
今天又学习到了新知识!
---------------------------------------------------------------------------------------------------------------------------------
文章借鉴于田螺哥的公众号