Future不支持回调怎么办?使用CompletableFuture

代码参考: https://gitee.com/constfafa/data_structure_and_algorithm/tree/master/java-concurrency

package wangwenjun.phase3.executor.completablefuture.demo02

背景:

spring boot web项目

controller要调用同一个service下的4个服务

目前采用的是串行方式

代码参考:wangwenjun.phase3.executor.completablefuture.demo02.AppV1Test#test

String items = esService.searchQueryItems("xxx");
String guides = esService.searchQueryGuides("xxx");
String applications = esService.getApplications("xxx");
String appoints = esService.getAppointItems("xxx");

串行执行很耗时,如何用多线程来优化呢?

方法1:单例ThreadpoolExecutor#Submit Callable

代码参考:wangwenjun.phase3.executor.completablefuture.demo02.AppV1Test#test4

        Callable<String> itemResult = () -> esService.searchQueryItems(keyword);
        Callable<String> guideResult = () -> esService.searchQueryGuides(keyword);
        Callable<String> appResult = () -> esService.getApplications(keyword);
        Callable<String> appointResult = () -> esService.getAppointItems(keyword);

        // 使用线程池#submit方法确实是并发执行了
        ThreadPoolExecutor executor = SearchRequestThreadPool.getExecutor();
        Future<String> itemFuture = executor.submit(itemResult);
        Future<String> guideFuture = executor.submit(guideResult);
        Future<String> appFuture = executor.submit(appResult);
        Future<String> appointFuture = executor.submit(appointResult);

        // 获取结果仍旧是串行的 原因就在于Future没有提供回调的方法
        list.add(itemFuture.get(3, TimeUnit.SECONDS));
        list.add(guideFuture.get(3, TimeUnit.SECONDS));
        list.add(appFuture.get(3, TimeUnit.SECONDS));
        list.add(appointFuture.get(3, TimeUnit.SECONDS));

方法2:FutureTask + 单例ThreadpoolExecutor

代码参考:wangwenjun.phase3.executor.completablefuture.demo02.AppV3Test#test

// 创建4个futureTask
​
// 用4个线程来执行
        ThreadPoolExecutor threadPoolExecutor = SearchRequestThreadPool.getExecutor();
        threadPoolExecutor.execute(itemsFutureTask);
        threadPoolExecutor.execute(guidesFutureTask);
        threadPoolExecutor.execute(applicationsFutureTask);
        threadPoolExecutor.execute(appointsFutureTask);
​
// 获取结果 仍旧是串行的
items = itemsFutureTask.get(2, TimeUnit.SECONDS);
guides = guidesFutureTask.get(2, TimeUnit.SECONDS);
applications = applicationsFutureTask.get(2, TimeUnit.SECONDS);
appoints = appointsFutureTask.get(2, TimeUnit.SECONDS);

方法1和2存在的问题:查询是并行的 但是由于futureTask#get是串行的 所以是无法保证整个请求在执行时间内返回

对于方法1和方法2可以进行修改

代码参考:wangwenjun.phase3.executor.completablefuture.demo02.AppV1Test#test5

        Callable<String> itemResult = () -> esService.searchQueryItems(keyword);
        Callable<String> guideResult = () -> esService.searchQueryGuides(keyword);
        Callable<String> appResult = () -> esService.getApplications(keyword);
        Callable<String> appointResult = () -> esService.getAppointItems(keyword);

        // 使用线程池#submit方法确实是并发执行了
        ThreadPoolExecutor executor = SearchRequestThreadPool.getExecutor();
        Future<String> itemFuture = executor.submit(itemResult);
        Future<String> guideFuture = executor.submit(guideResult);
        Future<String> appFuture = executor.submit(appResult);
        Future<String> appointFuture = executor.submit(appointResult);

        // 启动4个线程并发执行 并且通过join方式
        Thread thread01 = new Thread(() -> {
            try {
                list.add(itemFuture.get(3, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }, "my-thread-1");
        thread01.start();

        Thread thread02 = new Thread(() -> {
            try {
                list.add(guideFuture.get(3, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }, "my-thread-2");
        thread02.start();

        Thread thread03 = new Thread(() -> {
            try {
                list.add(appFuture.get(3, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }, "my-thread-3");
        thread03.start();

        Thread thread04 = new Thread(() -> {
            try {
                list.add(appointFuture.get(3, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }, "my-thread-04");
        thread04.start();

        try {
            thread01.join();
            thread02.join();
            thread03.join();
            thread04.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

可以看出来,这样的成本是很高的,每次请求要使用8个线程来执行,线程池中的4个线程用来计算,自己启动的4个线程用来执行add操作。根本原因就是Future不支持回调。

方法3:使用CountDownLatch

代码参考:wangwenjun.phase3.executor.completablefuture.demo02.AppV4Test#test

// 使用全局线程池
        ThreadPoolExecutor threadPoolExecutor = SearchRequestThreadPool.getExecutor();
        threadPoolExecutor.execute(new QueryThread(countDownLatch, esService, "searchQueryItems",
                new Class<?>[]{String.class}, new
                Object[]{"changliang"}, list));
        threadPoolExecutor.execute(new QueryThread(countDownLatch, esService, "searchQueryGuides",
                new Class<?>[]{String.class}, new
                Object[]{"changliang"}, list));
        threadPoolExecutor.execute(new QueryThread(countDownLatch, esService, "getApplications",
                new Class<?>[]{String.class}, new
                Object[]{"changliang"}, list));
        threadPoolExecutor.execute(new QueryThread(countDownLatch, esService, "getAppointItems",
                new Class<?>[]{String.class}, new
                Object[]{"changliang"}, list));
​
            /**
             * 统一时间控制
             */
​
            countDownLatch.await(3,TimeUnit.SECONDS);

可见这种方式是可行的

方法4:使用CompletableFuture

代码参考:wangwenjun.phase3.executor.completablefuture.demo02.AppV5Test

CompletableFuture<Void> future = CompletableFuture.allOf(xxx);
​
future.get(5, TimeUnit.SECONDS);

总结来说,方法3和方法4都可以使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值