线程池整合CompletableFuture实现异步编程

线程池整合CompletableFuture实现异步编程

Gitee地址:https://gitee.com/yuyuuyuy/micro-mall



前言

线程池是处理一个异步任务或并发任务的重要手段,然而,如果多个线程存在依赖组合的话,那么我们就需要使用
CompletableFuture来对线程进行异步编排。使用到的技术有:线程池技术,CompletableFuture


一、异步编程方案探讨

场景说明:电商项目中,一个任务的执行会包含很多个子任务,比如查询商品详情页,我们需要获取sku的基本信息(0.5s),获取sku的图片信息(0.5s),获取sku的促销信息(1s),获取spu详情(1s),如果这一系列任务都是顺序执行的话,需要0.5+0.5+1+1=3s,但这样效率就太低了,这些任务其实都是可以异步执行的,如果用多个线程让这些任务异步执行,那么只需要1s,效率就大大提升了。然而,有些场景下,不是所有线程都适合异步执行的,比如线程B需要线程A执行完后才能执行,但是A线程和C线程可以异步执行,那么我们就需要使用CompletableFuture来对线程实现异步编排。

二、线程池和CompletableFuture介绍及效果展示

1.线程池

核心是设置七大参数
1.corePoolSize:线程池基本大小
2.maximumPoolSize:线程池最大大小
3.keepAliveTime:线程存活保持时间
4.timeUnit: 时间单位
5.workQueue:任务队列
6.threadFactory:线程工厂
7.handler:线程饱和策略

例子:一个线程池,corePoolSize=7,maximumPoolSize=20,workQueue=50,100个并发进来,
那么7个会立即执行,然后再开20-7=13个线程执行,50个线程进入队列等待执行,剩下100-20-50=30个就拒绝(如果拒绝策略是直接抛弃的话)
``

2.CompletableFuture异步编排

CompletableFuture使用步骤
1.CompletableFuture是基于线程池的,可以先自定义线程池,然后注入到CompletableFuture中
2.创建CompletableFuture对象,执行CompletableFuture的方法。
并行执行方法:
supplyAsync方法,
runAsync方法:
串行执行方法:
thenRun方法
thenRunAsync方法
thenAccept方法
thenAcceptAsync方法
thenApply方法
thenApplyAsync方法
解读:then就是串行化执行,没有then就是异步执行,Run方法没有参数没有返回值,Accept有入参无返回值,Apply有入参有返回值,有Async就表明会使用新线程来串行化方法,没有Async就表明会使用原来的线程执行串行化方法
3.对这些CompletableFuture对象进行多任务组合
anyOf(CompletableFuture<?>... cfs):只要有一个CompletableFuture对象执行完就往下执行 allOf(CompletableFuture<?>… cfs):全部的CompletableFuture对象执行完才继续往下执行

3.效果展示

1.串行化
场景说明:模拟商品信息串行化获取,且在自定义的线程池中执行,先查询商品id,再根据该商品id查询库存。
在这里插入图片描述
可见,商品id和库存是串行化执行的,但是这两线程和主线程是并行化执行的,所以main方法先结束后查库存方法才结束。
2.并行化
查询商品图片信息,查询商品属性,查询商品介绍并行化执行,其中查询商品属性等待3s才执行,
在这里插入图片描述
可见,虽然查询商品属性在代码里是第二个执行的,但是因为等待了3秒,所以查询商品介绍就先执行了,体现了线程的并行化
3.多任务组合
查询商品图片信息,查询商品属性,查询商品介绍并行化执行,其中查询商品属性等待3s才执行,使用anyOf方法,任何一个线程执行完都返回结果然后往下执行。
在这里插入图片描述
从结果可以看出,查询图片信息线程先执行完毕然后返回结果,其他线程接着执行

三、代码实现:

1.自定义线程池:

    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {

        return new ThreadPoolExecutor(20, 200, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
    }
   //线程池,10个线程
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);

2.串行化:

    /**
     * 模拟商品信息串行化获取,且在自定义的线程池中执行
     * 先查询商品id,再根据该商品id查询库存
     * thenRunAsync获取不到上一步的结果
     * thenAcceptAsync可获取上一步的结果
     */

    @Test
    public void getProBySerial() {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        System.out.println("===========main开始===========");
        final CompletableFuture<Void> Future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 1283;
            System.out.println("查商品id:" + i);
            return i;
        }, executorService).thenAcceptAsync((i) -> {
            int j = i - 1000;
            System.out.println("根据商品id" + i + "查库存,库存为:" + j);
        }, executorService);
        System.out.println("===========main结束===========");
    }

3.异步编排或并行化

    /**
     * 模拟异步获取商品信息,且在自定义的线程池中执行
     * allOf.get()可等全部执行完后,再一起获取全部信息
     * anyOf.get()在任何流程执行完后,都可返回信息
     */

    public void getProByParallel() throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        System.out.println("===========main开始===========");
        //模拟商品信息异步获取流程
        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品图片信息");
            return "查询商品图片信息";
        }, executorService);

        CompletableFuture<Void> futureAttr = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("查询商品属性");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }, executorService);

        CompletableFuture<Void> futureDesc = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品介绍");
            return null;
        }, executorService);

        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
//        CompletableFuture<Void> allOf=CompletableFuture.allOf(futureImg,futureAttr,futureDesc);
        System.out.println("最先执行完的是:" + anyOf.get());
        //        allOf.get();
        System.out.println("图片:" + futureImg.get() + ",属性:" + futureAttr.get() + ",描述:" + futureDesc.get());
        System.out.println("===========main结束===========");

    }

总结

使用线程池结合 CompletableFuture异步编排,我们可以大大提高线程的并发性,优化程序的执行效率。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,可以使用线程池CompletableFuture一起工作来提高应用程序的并发性能和响应性。 首先,你可以使用`ThreadPoolTaskExecutor`类来创建一个线程池。在Spring Boot中,你可以通过在应用程序的配置文件中添加以下属性来配置线程池: ```properties # 线程池配置 spring.task.execution.pool.core-size=10 spring.task.execution.pool.max-size=20 spring.task.execution.pool.queue-capacity=1000 spring.task.execution.pool.thread-name-prefix=my-thread- ``` 这个配置将创建一个具有10个核心线程和20个最大线程的线程池,以及一个容量为1000的工作队列。你可以根据你的需求进行调整。 接下来,你可以使用CompletableFuture来异步执行任务。CompletableFuture是一个Java 8引入的类,可以用于进行异步编程。你可以使用它的`supplyAsync()`方法来异步执行一个任务,并返回一个Future对象。 ```java CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { // 执行异步任务 return "任务结果"; }); ``` 你可以在CompletableFuture对象上调用`thenApply()`、`thenAccept()`或`thenRun()`等方法来处理异步任务的结果或执行其他操作。这些方法接收一个函数作为参数,用于处理任务的结果。 ```java future.thenApply(result -> { // 处理任务结果 return "处理后的结果"; }); ``` 在处理任务结果时,你可以利用线程池来执行一些耗时的操作,以避免阻塞主线程。你可以通过在CompletableFuture对象上调用`thenApplyAsync()`、`thenAcceptAsync()`或`thenRunAsync()`方法来异步执行操作。 ```java future.thenApplyAsync(result -> { // 异步处理任务结果 return "处理后的结果"; }, executor); ``` 在这里,`executor`是你之前配置的线程池对象。 通过将线程池CompletableFuture结合起来使用,你可以实现更高效的并发处理和响应性能。记得根据实际需求调整线程池的配置,以避免资源浪费或线程饥饿等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值