利用java8的CompletableFuture异步并行操作

本文介绍如何使用Java 8的CompletableFuture进行并行数据处理,通过实例展示如何从多个数据源并行获取数据并合并结果,显著提高执行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java lambda表达式

需求点:业务上常常有这样一个需求:一个服务常常会从多个数据源取得数据,然后并成一个结果。

这个操作,假设有3个数据源,同步处理通常的做法是:需要queryData1,queryData2,queryData3。执行时间会是3个时间之和。
​ 一般的异步设计方案为:起一个业务的线程池,并发执行业务,然后由一个守护的线程等各个业务结束(时间为业务执行最长的时间),获取所有数据。这样明显执行时间会小于3个业务时间之和(例如下面的getAllInfoByProductId)。因为:消耗时间=执行最长的业务时间+守护线程的时间。
例如用 jdk的Future和线程池实现类似功能,自己造了轮子各种调试。

​ 现在java8提供了一个很好的CompletableFuture工具,非常爽。Talk is cheap, show you the code:

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * 并行获取各个数据源的数据合并成一个数据组合
 */
public class ParallelTest {
    /**
     * 获取基本信息
     *
     * @return
     */
    public String getProductBaseInfo(String productId) {
        return productId + "商品基础信息";
    }

    /**
     * 获取详情信息
     *
     * @return
     */
    public String getProductDetailInfo(String productId) {
        return productId + "商品详情信息";
    }

    /**
     * 获取sku信息
     *
     * @return
     */
    public String getProductSkuInfo(String productId) {
        return productId + "商品sku信息";
    }

    /**
     * 取得一个商品的所有信息(基础、详情、sku)
     *
     * @param productId
     * @return
     */
    public String getAllInfoByProductId(String productId) {
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> getProductBaseInfo(productId));
        CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> getProductDetailInfo(productId));
        CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> getProductSkuInfo(productId));
        //等待三个数据源都返回后,再组装数据。这里会有一个线程阻塞
        CompletableFuture.allOf(f1, f2, f3).join();
        try {
            String baseInfo = f1.get();
            String detailInfo = f2.get();
            String skuInfo = f3.get();
            return baseInfo + "" + detailInfo + skuInfo;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return null;
    }
   /**
    *   并行获取数据的计算
    */
    @Test
    public void testGetAllInfoParalleByProductId() throws ExecutionException, InterruptedException {
        ParallelTest test = new ParallelTest();
        String info = test.getAllInfoByProductId("1111");
        Assertions.assertNotNull(info);
    }
   /**
    *   同步获取执行的处理
    */
    @Test
    public void testGetAllInfoDirectly() throws ExecutionException, InterruptedException {
        ParallelTest test = new ParallelTest();
        String info1 = getProductBaseInfo("1111");
        String info2 = getProductDetailInfo("1111");
        String info3 = getProductSkuInfo("1111");
        String info=baseInfo + "" + detailInfo + skuInfo;
        Assertions.assertNotNull(info);
    }
}

allOf是等待所有任务完成,接触阻塞,获取各个数据源的数据。
改进
对于上面的例子,使用了默认的线程池,线程数为cpu核数-1。这个并不能很好地利用资源。下面为线程数计算的公式:

服务器端最佳线程数量=((线程等待时间+线程cpu时间)/线程cpu时间) * cpu数量

下面例子中也将executor线程池暴露出来,方便配置线程数和做一些其他处理。

public class ParallelTest {
        ExecutorService executor = Executors.newFixedThreadPool(100);    
        /**
         * 取得一个商品的所有信息(基础、详情、sku)
         *
         * @param productId
         * @return
         */
        public String getAllInfoByProductId(String productId) {
            CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> getProductBaseInfo(productId),executor);
            CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> getProductDetailInfo(productId),executor);
            CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> getProductSkuInfo(productId),executor);
    
            try {
                String baseInfo = f1.get();
                String detailInfo = f2.get();
                String skuInfo = f3.get();
                return baseInfo + "" + detailInfo + skuInfo;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

当然还有其他的类似处理:
以前在处理类似问题的时候,用了twitter的一个util工具(scala语言实现)

<dependency>
	<groupId>com.twitter</groupId>
	<artifactId>util-core_2.11</artifactId>
	<version>${util-core_2.11.version}</version>
</dependency>

用这个工具包的Future实现了类似的功能,但是多加了一种语言依赖
参考:
https://blog.youkuaiyun.com/u011726984/article/details/79320004
http://iamzhongyong.iteye.com/blog/1924745

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值