异步编程Completablefuture使用详解----进阶篇

JDK版本:jdk17
IDEA版本:IntelliJ IDEA 2022.1.3


文章目录

  • 前言
  • 一、异步任务的交互
    • 1.1 applyToEither
    • 1.2 acceptEither
    • 1.3 runAfterEither
  • 二、get() 和 join() 区别
  • 三、ParallelStream VS CompletableFuture
    • 3.1 使用串行流执行并统计总耗时
    • 3.2 使用并行流执行并统计总耗时
    • 3.3 使用串行流和CompletableFutre组合执行并统计总耗时
    • 3.4 使用串行流和CompletableFutre组合执行并统计总耗时(优化:指定线程数量)
    • 3.5 注意合理配置线程池中的线程数
  • 四、大数据商品比价Demo(实践)
    • 4.1 需求描述和分析
    • 4.2 构建工具类和实体类
    • 4.3 构建 HttpRequest
    • 4.4 使用串行的方式操作商品比价
    • 4.5 使用Future+线程池增加并行
    • 4.6 使用CompletableFuture进一步增强并行
    • 4.7 需求变更:同一个平台比较同款产品(iPhone15)不同色系的价格


前言

在前面入门篇中的内容中,我们详细介绍了异步编程Completablefuture的基本用法等相关知识;接下来,在本文进阶篇我们将详细探讨Completablefuture与stream API 相结合的具体用法,以及进一步增强并行处理的相关知识和案例用法


提示:以下是本篇文章正文内容,下面案例可供参考

一、异步任务的交互

释义

异步任务的交互是指在异步任务获取结果的速度相比较中,按一定的规则(先到先得)进行下一步处理

1.1 applyToEither

说明

applyToEither() 把两个异步任务做比较,异步任务先得到结果的,就对其获得的结果进行下一步操作

在这里插入图片描述

案例

演示使用最先完成的异步任务的结果

示例代码如下

public static void main(String[] args) throws ExecutionException, InterruptedException {//异步任务1CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {int x = new Random().nextInt(3);CommonUtils.sleepSecond(x);CommonUtils.printTheadLog("任务1耗时" + x + "秒");return x;});//异步任务2CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {int y = new Random().nextInt(3);CommonUtils.sleepSecond(y);CommonUtils.printTheadLog("任务2耗时" + y + "秒");return y;});//哪个异步任务结果先到达,使用哪个异步任务的结果CompletableFuture<Integer> future3 = future1.applyToEither(future2, result -> {CommonUtils.printTheadLog("最先到达的是" + result);return result;});CommonUtils.sleepSecond(4);Integer ret = future3.get();CommonUtils.printTheadLog("ret ="+ret);//异步任务交互指两个异步任务,哪个结果先到,就使用哪个结果(先到先用)}

运行如下

在这里插入图片描述

以下是applyToEither 和其对应的异步回调版本

CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)
CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)

1.2 acceptEither

说明

acceptEither()把两个异步任务做比较,异步任务先到结果的,就对先到的结果进行下一步操作(消费使用)

在这里插入图片描述

以下是acceptEither和其对应的异步回调版本

CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor)

案例

演示使用最先完成的异步任务的结果

示例代码如下

public static void main(String[] args) {//异步任务1CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {int x = new Random().nextInt(3);CommonUtils.sleepSecond(x);CommonUtils.printTheadLog("任务1耗时" + x + "秒");return x;});//异步任务2CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {int y = new Random().nextInt(3);CommonUtils.sleepSecond(y);CommonUtils.printTheadLog("任务2耗时" + y + "秒");return y;});//哪个异步任务结果先到达,使用哪个异步任务的结果future1.acceptEither(future2,result-> {CommonUtils.printTheadLog("最先到达的是"+result);});CommonUtils.sleepSecond(4);}

运行如下

在这里插入图片描述

1.3 runAfterEither

说明

如果不关心最先到达的结果,只想在有一个异步任务完成时得到完成的通知,可以使用 runAfterEither()

在这里插入图片描述

以下是它的相关方法

CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action)
CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action)
CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor)

案例

演示在最先完成的异步任务时得到它完成的通知

示例代码如下

public static void main(String[] args) {//异步任务1CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {int x = new Random().nextInt(3);CommonUtils.sleepSecond(x);CommonUtils.printTheadLog("任务1耗时" + x + "秒");return x;});//异步任务2CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {int y = new Random().nextInt(3);CommonUtils.sleepSecond(y);CommonUtils.printTheadLog("任务2耗时" + y + "秒");return y;});future1.runAfterEither(future2, () -> {CommonUtils.printTheadLog("有一个异步任务执行完成");});CommonUtils.sleepSecond(4);/*** thenApply   thenAccept    thenRun* 对上一个异步任务的结果进行操作(转换、消费使用等)**applyToEither    acceptEither   runAfterEither* 对两个异步任务先到的结果进行燥作(转换,消费使用)*/}

运行如下

在这里插入图片描述
Trips

在之前入门篇的文章中,我们学习了CompletableFuture的thenApply
,thenAccept与thenRun()的使用,它们和以上的applyToEither ,acceptEither与
runAfterEither看似都在对异步任务的结果进行燥作(转换,消费使用),而实则针对操作对象的结果存在不同

  • thenApply() ,thenAccept() ,thenRun()对上一个异步任务的结果进行操作(转换、消费使用等)
  • applyToEither() ,acceptEither(),runAfterEither()对两个异步任务先到的结果进行燥作(转换,消费使用)

二、get() 和 join() 区别

说明

get() 和 join() 都是CompletableFuture提供的以阻塞方式获取结果的方法

那么该如何选用呢? 请看如下案例:

public class GetOrJoinDemo {public static void main(String[] args) {// get or joinCompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {return "hello";});String ret = null;// 抛出检查时异常,必须处理try {String ret = future.get();} catch (InterruptedException e) {throw new RuntimeException(e);} catch (ExecutionException e) {throw new RuntimeException(e);}System.out.println(("ret = " + ret);// 抛出运行时异常,可以不处理String ret = future.join();System.out.println(("ret = " + ret);}
}

结论

使用时,我们发现,get() 抛出检查时异常,需要程序必须处理;而join() 方法抛出运行时异常,程序可以不处理。所以, join()更适合用在流式编程中


三、ParallelStream VS CompletableFuture

思考

  • CompletableFuture 虽然提高了任务并行处理能力,如果它和 Stream API 结合使用,能否进一步多个任务的并行处理能力呢?
  • Stream API 本身就提供了并行流 ParallelStream,CompletableFuture和它之间有什么不同呢

我们将通过一个耗时的任务来体现它们的不同, 更重要地是,我们能进一步加强 CompletableFuture 和 Stream API的结合使用,同时搞清楚 CompletableFuture 在流式操作的优势

案例

创建10个 MyTask 耗时的任务, 统计它们执行完的总耗时

准备工作

定义一个 MyTask 类,来模拟耗时的长任务

示例代码如下

public class MyTask {private int duration;public MyTask(int duration) {this.duration = duration;}// 模拟耗时的长任务public int doWork() {CommonUtils.printTheadLog("doWork");CommonUtils.sleepSecond(duration);return duration;}
}

3.1 使用串行流执行并统计总耗时

方案1

在主线程中使用串行流执行

示例代码如下

 public static void main(String[] args) {//需求:创建10个MyTask耗时的任务,统计它们执行完的总耗时//方案一:在主线程中使用串行执行//step 1: 创建1日个MyTask对象,每个任务持续1s,存入List集合IntStream intStream = IntStream.range(0, 10);List<MyTask> tasks = intStream.mapToObj(item -> {return new MyTask(1);}).collect(Collectors.toList());//step 2: 执行10个MyTask,统计总耗时long start = System.currentTimeMillis();List<Integer> results = tasks.stream().map(myTask -> {return myTask.doWork();}).collect(Collectors.toList());long end = System.currentTimeMillis();double costTime = (end - start) / 1000.0;System.out.printf("processed %d tasks %.2f second", tasks.size(), costTime);}

运行如下

在这里插入图片描述
统计总耗时

10.12 second

3.2 使用并行流执行并统计总耗时

方案2

使用并行流执行统计

示例代码如下

public static void main(String[] args) {//需求:创建10个MyTask耗时的任务,统计它们执行完的总耗时//方案二:使用并行流//step 1: 创建1日个MyTask对象,每个任务持续1s,存入List集合IntStream intStream = IntStream.range(0, 10);List<MyTask> tasks = intStream.mapToObj(item -> {return new MyTask(1);}).collect(Collectors.toList());//step 2: 执行10个MyTask,统计总耗时long start = System.currentTimeMillis();List<Integer> results = tasks.parallelStream().map(myTask -> {return myTask.doWork();}).collect(Collectors.toList());long end = System.currentTimeMillis();double costTime = (end - start) / 1000.0;System.out.printf("processed %d tasks %.2f second", tasks.size(), costTime);}

运行如下

在这里插入图片描述

统计总耗时

3.04 second

3.3 使用串行流和CompletableFutre组合执行并统计总耗时

方案3

用并行流和CompletableFutre组合使用

示例代码如下

public static void main(String[] args) {//需求:创建10个MyTask耗时的任务,统计它们执行完的总耗时//方案三:使用并行流和CompletableFutre组合使用//step 1: 创建1日个MyTask对象,每个任务持续1s,存入List集合IntStream intStream = IntStream.range(0, 10);List<MyTask> tasks = intStream.mapToObj(item -> {return new MyTask(1);}).collect(Collectors.toList());//step 2: 根据MyTask对象构建10个耗时的异步任务long start = System.currentTimeMillis();
//        List<CompletableFuture<Integer>> futures = tasks.parallelStream().map(myTask -> {
//            return CompletableFuture.supplyAsync(() -> {
//                return myTask.doWork();
//            });
//        }).collect(Collectors.toList());List<CompletableFuture<Integer>> futures = tasks.stream().map(myTask -> {return CompletableFuture.supplyAsync(() -> {return myTask.doWork();});}).collect(Collectors.toList());//step 3: 当所有任务完成时,获取每个异步任务的执行结果,存入L1st集合中List<Integer> results = futures.stream().map(future -> {return future.join();}).collect(Collectors.toList());long end = System.currentTimeMillis();double costTime = (end - start) / 1000.0;System.out.printf("processed %d tasks %.2f second", tasks.size(), costTime);}

运行如下

在这里插入图片描述
统计总耗时

4.05 second

发现

本人使用CompletableFutre执行比之前使用并行流多花了约1.01秒的时间

本机CPU相关的核数配置如下

在这里插入图片描述

根据上述运行对比,CompletableFutre与并行流二者使用的时间大致一样,能否进一步优化呢?

CompletableFuture 比 ParallelSteam 优点之一是你可以指定Excutor去处理任务。你能选择更合适数量的线程。我们可以选择大于Runtime.getRuntime().availableProcessors()
数量的线程, 如下所示

3.4 使用串行流和CompletableFutre组合执行并统计总耗时(优化:指定线程数量)

示例代码如下

public static void main(String[] args) {// CompletableFuture 在流式操作中的优势// 需求: 创建10个 MyTask 耗时的任务, 统计它们执行完的总耗时// 方案四:使用CompletableFuture(指定线程数量)// step 1: 创建10个MyTask对象,每个任务持续1s, 存入List集合IntStream intStream = IntStream.range(0, 10);List<MyTask> tasks = intStream.mapToObj(item -> {return new MyTask(1);}).collect(Collectors.toList());// 准备线程池int N_CPU = Runtime.getRuntime().availableProcessors();// 设置线程池中的线程的数量至少为10ExecutorService executor = Executors.newFixedThreadPool(Math.min(tasks.size(),N_CPU * 2));// step 2: 根据MyTask对象构建10个异步任务List<CompletableFuture<Integer>> futures = tasks.stream().map(myTask -> {return CompletableFuture.supplyAsync(()-> {return myTask.doWork();},executor);}).collect(Collectors.toList());// step 3: 执行异步任务,执行完成后,获取异步任务的结果,存入List集合中,统计总耗时long start = System.currentTimeMillis();List<Integer> results = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());long end = System.currentTimeMillis();double costTime = (end - start) / 1000.0;System.out.printf("processed %d tasks %.2f second", tasks.size(), costTime);// 关闭线程池executor.shutdown();/*** 总结* CompLetabLeFuture可以控制更多的线程数量,而ParalLelstream不能*/}

运行如下

在这里插入图片描述
统计总耗时

2.02 second

Trips

测试运行时,本机电脑配置是4核4线程,而我们创建的线程池中线程数最少也是10个,所以每个线程负责一个任务(耗时1s)总体来说, 处理10个任务就得分成这样执行(第一次先分4个线程并行处理四个任务;第二次也分4个线程并行处理四个任务;第三次分两个线程并行处理剩下的l两个任务),故总共需要2.02秒

3.5 注意合理配置线程池中的线程数

正如我们看到的,CompletableFuture 可以更好的控制线程池的数量,而 parallelStream 不能

问题1:如何选用 CompletableFuture 和 ParallelStream?

  • 如果你的任务是IO密集型,你应该使用 CompletableFuture;
  • 如果你的任务是CPU密集型,使用比处理器更多的线程是没有意义的,所以选择 ParallelSteam,因为它不需要创建线程池,更容易使用。

问题2:IO密集型任务和CPU密集型任务的区别

CPU密集型也叫计算密集型,此时,系统运行时大部分的状况是CPU占用率近乎100%,I/O在很短的时间可以完成,而CPU还有许多运算要处理,CPU使用率很高。比如计算1+2+3…+10万亿、天文计算、圆周率后几十位等,都属于CPU密集型程序。

CPU密集型任务的特点:大量计算,CPU占用率一般都很高,I/O时间很短

IO密集型指大部分的状况是CPU在等I/O(硬盘/内存)的读写操作,但CPU的使用率不高。

简单的说,就是需要大量的输入输出,例如读写文件、传输文件,网络请求。 IO密集型任务的特点:大量网络请求,文件操作,CPU运算少,很多时候CPU在等待资源才能进一步操作。

问题3:既然要控制线程池的数量,多少合适呢?

  • 如果是CPU密集型任务,就需要尽量压榨CPU,参数值可以设为 Ncpu + 1
  • 如果是IO密集型任务,参考值可以设置为 2 * Ncpu,其中 Ncpu 表示核心数

注意

以上给的是业界认为的参考值, 详细配置的问题以后再行讨论,故不在赘述


四、大数据商品比价Demo(实践)

4.1 需求描述和分析

实现一个大数据比价服务,价格数据可以从京东、天猫、拼多多等平台去获取指定商品的价格、优惠金额,然后计算出实际付款金额(商品价格 -优惠金额),最终返回价格最优的平台与价格信息

在这里插入图片描述

4.2 构建工具类和实体类

①定义价格实体类 PriceResult

示例代码如下

public class PriceResult {private int price;private int discount;private int realPrice;private String platform;public PriceResult() {}public PriceResult(String platform) {this.platform = platform;}public PriceResult(int price, int discount, int realPrice, String platform) {this.price = price;this.discount = discount;this.realPrice = realPrice;this.platform = platform;}public int getPrice() {return price;}public void setPrice(int price) {this.price = price;}public int getDiscount() {return discount;}public void setDiscount(int discount) {this.discount = discount;}public int getRealPrice() {return realPrice;}public void setRealPrice(int realPrice) {this.realPrice = realPrice;}public String getPlatform() {return platform;}public void setPlatform(String platform) {this.platform = platform;}@Overridepublic String toString() {return "PriceResult{" +"平台='" + platform + '\'' +", 平台价=" + price +", 优惠价=" + discount +", 最终价=" + realPrice +'}';}
}

②修改工具类CommonUtils, 添加getCurrenTime()方法获取当前时间并格式化,添加 printThreadLog1()方法,在原先printThreadLog()方法的基础上把时间戳换成当前时间

示例代码如下

public class CommonUtils {// 读取文件路径的文件public static String readFile(String pathToFile) {try {return Files.readString(Paths.get(pathToFile));} catch (IOException e) {e.printStackTrace();return "";}}// 休眠指定的毫秒数public static void sleepMillis(long millis) {try {TimeUnit.MILLISECONDS.sleep(millis);} catch (InterruptedException e) {e.printStackTrace();}}// 休眠指定的秒数public static void sleepSecond(long second) {try {TimeUnit.SECONDS.sleep(second);} catch (InterruptedException e) {e.printStackTrace();}}//获取当前时间private static String getCurrentTime() {LocalTime now = LocalTime.now();return now.format(DateTimeFormatter.ofPattern("[HH:mm::ss.SS"));}// 打印输出带线程信息的日志public static void printTheadLog(String message) {// 时间戳 | 线程id | 线程名 | 日志信息String result = new StringJoiner(" | ").add(String.valueOf(System.currentTimeMillis())).add(String.format("%2d", Thread.currentThread().getId())).add(Thread.currentThread().getName()).add(message).toString();System.out.println(result);}// 打印输出带线程信息的日志public static void printTheadLog1(String message) {// 当前时间 | 线程id | 线程名 | 日志信息String result = new StringJoiner(" | ").add(getCurrentTime()).add(String.format("%2d", Thread.currentThread().getId())).add(Thread.currentThread().getName()).add(message).toString();System.out.println(result);}}

4.3 构建 HttpRequest

HttpRequest 用于模拟网络请求(耗时的操作)

示例代码如下

public class HttpRequest {private static void mockCostTimeOperation() {CommonUtils.sleepSecond(1);}// 获取淘宝平台的商品价格public static PriceResult getTaobaoPrice(String productName) {CommonUtils.printTheadLog("获取淘宝上" + productName + "价格");mockCostTimeOperation();PriceResult priceResult = new PriceResult("淘宝");priceResult.setPrice(5199);CommonUtils.printTheadLog("获取淘宝上" + productName + "价格完成:5199");return priceResult;}// 获取淘宝平台的优惠public static int getTaoBaoDiscount(String productName) {CommonUtils.printTheadLog("获取淘宝上" + productName + "优惠");mockCostTimeOperation();CommonUtils.printTheadLog("获取淘宝上" + productName + "优惠完成:-200");return 200;}// 获取京东平台的商品价格public static PriceResult getJDongPrice(String productName) {CommonUtils.printTheadLog1("获取京东上" + productName + "价格");mockCostTimeOperation();PriceResult priceResult = new PriceResult("淘宝");priceResult.setPrice(5299);CommonUtils.printTheadLog1("获取京东上" + productName + "价格完成:5299");return priceResult;}// 获取京东平台的优惠public static int getJDongDiscount(String productName) {CommonUtils.printTheadLog1("获取京东上" + productName + "优惠");mockCostTimeOperation();CommonUtils.printTheadLog1("获取京东上" + productName + "优惠完成:-150");return 150;}// 获取拼多多平台的商品价格public static PriceResult getPDDPrice(String productName) {CommonUtils.printTheadLog1("获取拼多多上" + productName + "价格");mockCostTimeOperation();PriceResult priceResult = new PriceResult("拼多多");priceResult.setPrice(5399);CommonUtils.printTheadLog1("获取拼多多上" + productName + "价格完成:5399");return priceResult;}// 获取拼多多平台的优惠public static int getPDDDiscount(String productName) {CommonUtils.printTheadLog1("获取拼多多上" + productName + "优惠");mockCostTimeOperation();CommonUtils.printTheadLog1("获取拼多多上" + productName + "优惠完成:-5300");return 5300;}}

4.4 使用串行的方式操作商品比价

①ComparePriceService类代码如下

public class ComparePriceService {// 方案一:串行方式操作商品比价public PriceResult getCheapestPlatformPrice(String productName) {PriceResult priceResult;int discount;// 获取淘宝平台的商品价格和优惠priceResult = HttpRequest.getTaobaoPrice(productName);discount = HttpRequest.getTaoBaoDiscount(productName);PriceResult taoBaoPriceResult = this.computeRealPrice(priceResult, discount);// 获取京东平台的商品价格和优惠priceResult = HttpRequest.getJDongPrice(productName);discount = HttpRequest.getJDongDiscount(productName);PriceResult jDongPriceResult = this.computeRealPrice(priceResult, discount);// 获取拼多多平台的商品价格和优惠priceResult = HttpRequest.getPDDPrice(productName);discount = HttpRequest.getPDDDiscount(productName);PriceResult pddPriceResult = this.computeRealPrice(priceResult, discount);// 计算最优的平台和价格Stream<PriceResult> stream = Stream.of(taoBaoPriceResult, jDongPriceResult, pddPriceResult);Optional<PriceResult> minOpt = stream.min(Comparator.comparing(priceRes -> {return priceRes.getRealPrice();}));PriceResult result = minOpt.get();return result;
//        return Stream.of(taoBaoPriceResult, jDongPriceResult, pddPriceResult)
//                .min(Comparator.comparing(PriceResult::getRealPrice))
//                .get();}// 计算商品的最终价格 = 平台价格 - 优惠价public PriceResult computeRealPrice(PriceResult priceResult, int discount) {priceResult.setRealPrice(priceResult.getPrice() - discount);priceResult.setDiscount(discount);CommonUtils.printTheadLog(priceResult.getPlatform() + "最终价格计算完成" + priceResult.getRealPrice());return priceResult;}}

②main方法中执行

示例代码如下

public static void main(String[] args) {//1. 使用串行方式操作商品比价ComparePriceService service = new ComparePriceService();long start = System.currentTimeMillis();PriceResult priceResult = service.getCheapestPlatformPrice("iphone16");long end = System.currentTimeMillis();double costTime = (end - start)/1000.0;System.out.printf("cost %.2f second processed\n",costTime);System.out.println("priceResult = " + priceResult);}

运行如下

在这里插入图片描述
统计总耗时

6.11 second

4.5 使用Future+线程池增加并行

①ComparePriceService类代码如下

public class ComparePriceService {//方案二:使用Future + 线程池 操作商品比价public PriceResult getCheapestPlatformPrice2(String productName) {// 线程池ExecutorService executor = Executors.newFixedThreadPool(4);// 获取淘宝平台的商品价格和优惠Future<PriceResult> taobaoFuture = executor.submit(() -> {PriceResult priceResult = HttpRequest.getTaobaoPrice(productName);int discount = HttpRequest.getTaoBaoDiscount(productName);return this.computeRealPrice(priceResult, discount);});// 获取京东平台的商品价格和优惠Future<PriceResult> jdFuture = executor.submit(() -> {PriceResult priceResult = HttpRequest.getJDongPrice(productName);int discount = HttpRequest.getJDongDiscount(productName);return this.computeRealPrice(priceResult, discount);});// 获取拼多多平台的商品价格和优惠Future<PriceResult> pddFuture = executor.submit(() -> {PriceResult priceResult = HttpRequest.getPDDPrice(productName);int discount = HttpRequest.getPDDDiscount(productName);return this.computeRealPrice(priceResult, discount);});// 计算最优的平台和价格PriceResult priceResult = Stream.of(taobaoFuture, jdFuture, pddFuture).map(item -> {try {//假设延时5s后,就不要它的结果,所以返回一个空return item.get(5, TimeUnit.SECONDS);} catch (Exception e) {e.printStackTrace();return null;}finally {executor.shutdown();}}).filter(Objects::nonNull).min(Comparator.comparing(PriceResult::getRealPrice)).get();return priceResult;}// 计算商品的最终价格 = 平台价格 - 优惠价public PriceResult computeRealPrice(PriceResult priceResult, int discount) {priceResult.setRealPrice(priceResult.getPrice() - discount);priceResult.setDiscount(discount);CommonUtils.printTheadLog(priceResult.getPlatform() + "最终价格计算完成" + priceResult.getRealPrice());return priceResult;}}

②main方法中执行

示例代码如下

public static void main(String[] args) {//2.使用Future + 线程池 操作商品比价ComparePriceService service = new ComparePriceService();long start = System.currentTimeMillis();PriceResult priceResult = service.getCheapestPlatformPrice2("iphone16");long end = System.currentTimeMillis();double costTime = (end - start)/1000.0;System.out.printf("cost %.2f second processed\n",costTime);System.out.println("priceResult = " + priceResult);
}

运行如下

在这里插入图片描述
统计总耗时

2.05 second

4.6 使用CompletableFuture进一步增强并行

①ComparePriceService类代码如下

public class ComparePriceService {//方案三:使用CompletableFuture 操作商品比价public PriceResult getCheapestPlatformPrice3(String productName) {// 获取淘宝平台的商品价格和优惠CompletableFuture<PriceResult> taobaofuture = CompletableFuture.supplyAsync(() -> HttpRequest.getTaobaoPrice(productName)).thenCombine(CompletableFuture.supplyAsync(() -> HttpRequest.getTaoBaoDiscount(productName)), (priceRsult, discount) -> {return this.computeRealPrice(priceRsult, discount);});// 获取京东平台的商品价格和优惠CompletableFuture<PriceResult> jdfuture = CompletableFuture.supplyAsync(() -> HttpRequest.getJDongPrice(productName)).thenCombine(CompletableFuture.supplyAsync(() -> HttpRequest.getJDongDiscount(productName)), (priceRsult, discount) -> {return this.computeRealPrice(priceRsult, discount);});// 获取拼多多平台的商品价格和优惠CompletableFuture<PriceResult> pddfuture = CompletableFuture.supplyAsync(() -> HttpRequest.getPDDPrice(productName)).thenCombine(CompletableFuture.supplyAsync(() -> HttpRequest.getPDDDiscount(productName)), (priceRsult, discount) -> {return this.computeRealPrice(priceRsult, discount);});// 计算最优的平台和价格PriceResult priceResult = Stream.of(taobaofuture, jdfuture, pddfuture).map(future -> future.join()).min(Comparator.comparing(item -> item.getRealPrice())).get();return  priceResult;}// 计算商品的最终价格 = 平台价格 - 优惠价public PriceResult computeRealPrice(PriceResult priceResult, int discount) {priceResult.setRealPrice(priceResult.getPrice() - discount);priceResult.setDiscount(discount);CommonUtils.printTheadLog(priceResult.getPlatform() + "最终价格计算完成" + priceResult.getRealPrice());return priceResult;}
}

②main方法中执行

示例代码如下

public static void main(String[] args) {//3.使用CompletableFuture 操作商品比价ComparePriceService service = new ComparePriceService();long start = System.currentTimeMillis();PriceResult priceResult = service.getCheapestPlatformPrice3("iphone15");long end = System.currentTimeMillis();double costTime = (end - start)/1000.0;System.out.printf("cost %.2f second processed\n",costTime);System.out.println("priceResult = " + priceResult);}

运行如下

在这里插入图片描述
统计总耗时

2.06 second

4.7 需求变更:同一个平台比较同款产品(iPhone15)不同色系的价格

说明

使用异步任务的批量操作实现

①ComparePriceService类代码如下

public class ComparePriceService {// 计算商品的最终价格 = 平台价格 - 优惠价public PriceResult computeRealPrice(PriceResult priceResult, int discount) {priceResult.setRealPrice(priceResult.getPrice() - discount);priceResult.setDiscount(discount);CommonUtils.printTheadLog(priceResult.getPlatform() + "最终价格计算完成" + priceResult.getRealPrice());return priceResult;}public PriceResult batchComparePrice(List<String> products) {// step 1:遍历每个商品的名字, 根据商品名称开启异步任务获取最终价, 归集到List集合中List<CompletableFuture<PriceResult>> futureList = products.stream().map(productName -> {return CompletableFuture.supplyAsync(() -> HttpRequest.getTaobaoPrice(productName)).thenCombine(CompletableFuture.supplyAsync(() -> HttpRequest.getTaoBaoDiscount(productName)), (priceRsult, discount) -> {return this.computeRealPrice(priceRsult, discount);});}).collect(Collectors.toList());// step 2: 把多个商品的最终价进行排序获取最小值PriceResult priceResult = futureList.stream().map(future -> future.join()).sorted(Comparator.comparing(item -> item.getRealPrice())).findFirst().get();return priceResult;}}

②main方法中执行

示例代码如下

public static void main(String[] args) {// 异步任务的批量操作// 测试在一个平台比较同款产品(iPhone15)不同色系的价格ComparePriceService service = new ComparePriceService();long start = System.currentTimeMillis();PriceResult priceResult = service.batchComparePrice(Arrays.asList("iphone15午夜黑","iphone15白色","iphone15淡青"));long end = System.currentTimeMillis();double costTime = (end - start)/1000.0;System.out.printf("cost %.2f second processed\n",costTime);System.out.println("priceResult = " + priceResult);
}

运行如下

在这里插入图片描述
统计总耗时

2.05 second


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/665262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端JavaScript篇之map和Object的区别、map和weakMap的区别

目录 map和Object的区别map和weakMap的区别 map和Object的区别 Object是JavaScript的内置对象&#xff0c;用于存储键值对。Object的键必须是字符串或符号&#xff0c;值可以是任意类型。Map是ES6引入的新数据结构&#xff0c;用于存储键值对。Map的键可以是任意类型&#xff…

C++ 日期类的实现

目录 前言 日期类中的成员函数和成员变量 日期类中成员函数的详解和实现 1.天数前后的判断 2.天数加减的实现 3.前置 && 后置 4.计算天数差值 前言 日期类的实现将综合前面所学的&#xff08;类的6个默认成员函数&#xff09;&#xff0c;进一步理解和掌握类的…

COX预测模型过程中,我踩过的那些雷

R语言做&#xff01;初学者先进来看看&#xff01;&#xff01;&#xff01; SCI冲 COX分析&#xff1a;做临床信息与预后相关的COX分析大致都会分为两个步骤&#xff0c;先做单因素COX回归分析&#xff0c;再根据P值挑选有意义的变量&#xff0c;最终纳入COX多因素回归模型中&…

20240202在WIN10下部署faster-whisper

20240202在WIN10下部署faster-whisper 2024/2/2 12:15 前提条件&#xff0c;可以通过技术手段上外网&#xff01;^_ 首先你要有一张NVIDIA的显卡&#xff0c;比如我用的PDD拼多多的二手GTX1080显卡。【并且极其可能是矿卡&#xff01;】800&#xffe5; 2、请正确安装好NVIDIA最…

SpringBoot实战项目第一天

环境搭建 后端部分需要准备&#xff1a; sql数据库 创建SpringBoot工程&#xff0c;引入对应的依赖(web\mybatis\mysql驱动) 配置文件application.yml中引入mybatis的配置信息 创建包结构&#xff0c;并准备实体类 完成今日开发后项目部分内容如下图示 用户注册于登录部分…

[BUUCTF]-PWN:mrctf2020_easy_equation解析

查看保护 再看ida 很明了&#xff0c;题目就是让我们用格式化字符串漏洞修改judge的值&#xff08;可以用python脚本进行计算&#xff0c;最终算出来得2&#xff09;使等式成立&#xff0c;然后getshell。 虽然操作比较简单&#xff0c;但我还是列出了几种方法 解法一&#x…

uni-app移动端图片预览组件 movable-area 、movable-view (支持缩放,拖动效果、替换部分代码图片可直接使用)

UniApp图片预览组件 利用uni-app官方<movable-area>、<movable-view>内置视图组件 配合 uView 组件的u-popup 弹框组件共同实现封装的图片预览组件&#xff0c;支持手指缩放、拖动效果&#xff0c;替换代码中部分图片后 可以直接使用。 效果图&#xff1a; 组件代码…

【数据结构与算法】——单链表的原理及C语言实现

数据结构与算法——链表原理及C语言实现 链表的原理链表的基本属性设计创建一个空链表链表的遍历&#xff08;显示数据&#xff09;释放链表内存空间 链表的基本操作设计&#xff08;增删改查&#xff09;链表插入节点链表删除节点链表查找节点增删改查测试程序 链表的复杂操作…

Vulnhub billu b0x

0x01 环境搭建 1. 从官方下载靶机环境&#xff0c;解压到本地&#xff0c;双击OVF文件直接导入到vmware虚拟机里面。2. 将虚拟机的网络适配器调成NAT模式&#xff0c;然后开机即可进行操作了。 0x02 主机发现 nmap -sn 192.168.2.0/24 成功获取靶机IP为192.168.2.129。 0x0…

本次安装Visual Studio 所用的安装程序不完整。请重新运行VisualStudio安装程序以解决此问题

今天点开VS的时候遇到了这个问题 因为昨天升级到一半电脑关机了&#xff0c;今天打开软件遇到如下错误&#xff0c; 解决办法很简单&#xff0c;找到安装目录进入Installer文件夹 我的目录在C:\Program Files (x86)\Microsoft Visual Studio\Installer 找到vs_installer.exe…

【python】python爱心代码

一、实现效果&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 二、准备工作 &#xff08;1)、导入必要的模块&#xff1a; 代码首先导入了需要使用的模块&#xff1a;requests、lxml和csv。 import requests from lxml import etree import csv 如果出现…

C#写个小工具,把多个word文档进行合并成一个word文档

先要安装包 帮助类WordDocumentMerger&#xff0c;用于处理word合并功能 using System; using System.Collections.Generic; using System.Text; using Microsoft.Office.Interop.Word; using System.Reflection; using System.IO; using System.Diagnostics;namespace WordH…

分别用JavaScript,Java,PHP,C++实现桶排序的算法(附带源码)

桶排序是计数排序的升级版。它利用了函数的映射关系&#xff0c;高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效&#xff0c;我们需要做到这两点&#xff1a; 在额外空间充足的情况下&#xff0c;尽量增大桶的数量使用的映射函数能够将输入的 N 个数据均匀的分…

【C语言】字符串函数介绍

目录 前言&#xff1a; 1. strlen 函数 函数介绍 strlen 函数的使用 strlen 函数的模拟实现 2. strcpy 函数 函数介绍 strcpy 函数的使用 strcpy 函数的模拟实现 3. strcat 函数 函数介绍 strcat 函数的使用 strcat 函数的模拟实现 4. strcmp 函数 函数介绍 st…

4K Video Downloader forMac/win:畅享高清视频下载的终极利器!

在如今的数字时代&#xff0c;高清视频已经成为人们生活中不可或缺的一部分。无论是观看精彩的电影、音乐视频&#xff0c;还是学习教育类的在线课程&#xff0c;我们都希望能够以最清晰流畅的方式来欣赏。而为了满足这一需求&#xff0c;我们需要一款功能强大的高清视频下载软…

工业平板电脑定制_三防平板电脑安卓主板厂家

工业平板电脑具有IP68级三防品质&#xff0c;采用高强度工业材质制造&#xff0c;结构稳固坚韧&#xff0c;具备较高的抗冲击和防震能力。隔空减震技术进一步加强了产品的抗冲击和防震动功能。广泛应用于工控、医疗、电信、电力、工业自动化设备、汽车检测、制造业等多个领域&a…

Flink实时数仓同步:快照表实战详解

一、背景 在大数据领域&#xff0c;初始阶段业务数据通常被存储于关系型数据库&#xff0c;如MySQL。然而&#xff0c;为满足日常分析和报表等需求&#xff0c;大数据平台采用多种同步方式&#xff0c;以适应这些业务数据的不同存储需求。这些同步存储方式包括离线仓库和实时仓…

MySQL数据库入门

MySQL数据库概述 1&#xff0c;为什么要使用数据库2&#xff0c;数据库的相关概念3&#xff0c;常见的数据库管理系统4&#xff0c;MySQL介绍5&#xff0c;关系型数据库和非关系型数据库6&#xff0c;关系型数据库的设计规则7&#xff0c;表的关联关系7.1&#xff0c;一对一7.2…

短剧小程序开发:打造高效、便捷的娱乐体验

随着移动互联网的普及和用户需求的多样化&#xff0c;短剧小程序作为一种新型的应用形态&#xff0c;逐渐受到了广大用户的青睐。短剧小程序开发旨在为用户提供一种高效、便捷的娱乐体验&#xff0c;让用户在忙碌的生活中轻松享受到精彩的短剧内容。本文将探讨短剧小程序开发的…

0203-2-输入输出系统

第六章&#xff1a;输入输出系统 I/O系统的功能&#xff0c;模型和接口 I/O系统管理的对象是I/O设备和相应的设备控制器。 I/O系统的基本功能 隐藏物理设备的细节与设备的无关性提高处理机和I/O设备的利用率对I/O设备进行控制确保对设备的正确共享错误处理 I/O软件的层次结…