CompletableFuture解决多线程返回结果问题

这篇具有很好参考价值的文章主要介绍了CompletableFuture解决多线程返回结果问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是CompletableFuture?

在Java中CompletableFuture用于异步编程,异步编程是编写非阻塞的代码,运行的任务在一个单独的线程,与主线程隔离,并且会通知主线程它的进度,成功或者失败。

在这种方式中,主线程不会被阻塞,不需要一直等到子线程完成。主线程可以并行的执行其他任务。

使用这种并行方式,可以极大的提高程序的性能。

Future vs CompletableFuture

CompletableFuture 是 Future API的扩展。

Future 被用于作为一个异步计算结果的引用。提供一个 isDone() 方法来检查计算任务是否完成。当任务完成时,get() 方法用来接收计算任务的结果。

从 Callbale和 Future 教程可以学习更多关于 Future 知识.

Future API 是非常好的 Java 异步编程进阶,但是它缺乏一些非常重要和有用的特性。

CompletableFuture 实现了 Future 和 CompletionStage接口,并且提供了许多关于创建,链式调用和组合多个 Future 的便利方法集,而且有广泛的异常处理支持。

之前项目中需要对一组集合进行处理,集合内的所有元素处理完后更新任务状态。当时经过询问得知可以用到CompletableFuture,于是经过短暂的研究写了一个很粗略的代码,如下:

public void calculateAvgSensorData() {
        //0.获取同步锁
        Boolean isLock = redisUtils.getLockWithExpire(LOCK_CAL_CABINET_AVG_ENV, LOCK_EXPIRE);
        if (!isLock) {
            log.warn("获取互斥锁: {}失败", LOCK_CAL_CABINET_AVG_ENV);
            return;
        }
        //1.查询昨日每个机柜一整天的环境数据均值,如果有报警,则用报警数据
        //1.1.获取当前日期
        Date today = DateUtil.date();
        //1.2.获取昨天日期
        Date yesterday = DateUtil.offsetDay(today, DAY_OFFSET);
        String queryDate = DateUtil.formatDate(yesterday);
        //1.3.查询机房列表
        List<BaseProps> roomList = roomService.getAllByUsed(1);
        //1.4.查询传感器列表
        List<Sensor> sensorList = sensorService.list();
        if (CollectionUtil.isEmpty(roomList) || CollectionUtil.isEmpty(sensorList)) {
            return;
        }
        //1.5.记录任务执行时间
        AutoTask autoTask = autoTaskService.getById(AutoTaskId.CAL_CABINET_AVG_ENV_TASK);
        autoTask.setTaskStatus(AutoTaskStatus.EXECUTING);
        HistoryAutoTask historyAutoTask = BeanUtil.copyProperties(autoTask, HistoryAutoTask.class);
        historyAutoTask.setExecuteTime(new Date());
        //记录任务开始时间
        historyAutoTaskService.startAutoTask(historyAutoTask);
        //1.6.组装待查询的机房队列
        roomList.forEach(room -> {
            queue.add(room);
        });

        for(int i = 0; i < MAX_THREAD; i++) {
            //创建异步执行任务
            CompletableFuture cf = CompletableFuture.runAsync(()->{
                do{
                    try {
                        queryCabinetEnv(sensorList, queryDate);
                    } catch (Exception e) {
                        log.error("插入机房环境数据均值失败,失败原因: {}", e);
                        historyAutoTask.setTaskStatus(AutoTaskStatus.FAILED);
                        historyAutoTaskService.updateAutoTask(historyAutoTask, AutoTaskStatus.EXECUTING);
                    }
                }while(queue.size() > 0);
            }, taskExecutor).whenComplete((res,excption)-> {
                //3.记录定时任务执行状态
                historyAutoTask.setTaskStatus(AutoTaskStatus.EXECUTED);
                historyAutoTask.setFinishTime(new Date());
                historyAutoTaskService.updateAutoTask(historyAutoTask, AutoTaskStatus.EXECUTING);
            });
        }
    }

如上代码,虽然用到了线程池,也用到了CompletableFuture,不过一眼可以看出问题所在,就是在每个遍历结束后都会更新一次任务状态,这明显是不对的。明明在所有任务执行完后再执行一次任务状态更新就可以了,这里却每个线程执行完任务后更新一次任务状态。

对于如上代码,肯定是有更加优雅的写法的,再次经过深入学习后,写出以下例子仅供改造参考:

package com.tct.ii.ucr.task;

import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author wl
 * @date 2022/10/12
 */
@Slf4j
public class TestFuture {
    public static CompletableFuture<String> printStr(String str, ExecutorService executorService) {
        return CompletableFuture.supplyAsync(() -> {
            log.info("str:{}", str);
            return str;
        }, executorService);
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        List<String> list = Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
        List<CompletableFuture<String>> futureList = list.stream()
                .map(str -> printStr(str, executorService)).collect(Collectors.toList());
        CompletableFuture<Void> allFuture = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()]));
        CompletableFuture<List<String>> resultFuture = allFuture.thenApply(v -> futureList.stream().map(future -> future.join()).collect(Collectors.toList()));
        log.info("result:{}", resultFuture.get());
        executorService.shutdown();
    }
}

在多任务组合中,allOf:等待所有任务完成,anyOf:只要有一个任务完成。

测试结果如图:

complablefuture 用多线程有返回值处理list,java,java,jvm,servlet

可以看到这里既用到了线程池,最后调用allOf方法等待所有任务执行完后可以一次性获取结果,非常方便和优雅。 文章来源地址https://www.toymoban.com/news/detail-574932.html

到了这里,关于CompletableFuture解决多线程返回结果问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • (已解决)关键词爬取百度搜索结果,返回百度安全验证,网络不给力,请稍后重试,无法请求到正确数据的问题(2023最新)

    已解决,使用进行百度搜索,然后爬取搜索结果,请求数据后,返回的是百度安全验证,网络不给力,请稍后重试。无法请求到正确数据。且尝试在header中增加Accept参数还是不行。      在学习过程中,写了一小段练习用的爬取程序,获取百度搜索后的结果,代

    2024年02月17日
    浏览(39)
  • 上传oss文件是出现 Unable to execute HTTP request: 返回结果无效,无法解析 错误解决办法

    项目中上传图片的时候有了一下错误: 那么这个错误我百度了一下这个错误 那个如果是关于OSS的错误,找不到的话可以查看一下阿里云OSS的官方文档查找一下 阿里云OSS文档      查看这些解决方案。我看这个发现我的都没问题,然后最后发现这个项目是我直接拉下来直接用

    2024年02月12日
    浏览(49)
  • CompletableFuture结合线程池初步使用

    CompletableFuture 是 Java 8 引入的一个类,用于支持异步编程和函数式编程。CompletableFuture 的优点包括: 异步编程:CompletableFuture 支持异步编程,可以在异步任务完成之前继续执行其他任务,从而提高程序的效率和吞吐量。 链式调用:CompletableFuture 提供了丰富的方法来支持链式调

    2024年02月09日
    浏览(34)
  • 多线程系列(二十) -CompletableFuture使用详解

    在上篇文章中,我们介绍了 Future 相关的用法,使用它可以获取异步任务执行的返回值。 我们再次回顾一下 Future 相关的用法。 运行结果如下: 如果不采用线程执行,那么总共用时应该会是 200 + 300 = 500 ms,而采用线程来异步执行,总共用时是 308 ms。不难发现,通过 Future 和

    2024年03月15日
    浏览(38)
  • SpringBoot多线程异步任务:ThreadPoolTaskExecutor + CompletableFuture

    在 SpringBoot 项目中,一个任务比较复杂,执行时间比较长,需要采用 多线程异步 的方式执行,从而缩短任务执行时间。 将任务拆分成多个独立的子任务,每个子任务在独立子线程中执行; 当所有子任务的子线程全部执行完成后,将几个子任务汇总,得到总任务的执行结果。

    2024年02月10日
    浏览(48)
  • Java的CompletableFuture,Java的多线程开发

    如下图: 以后用到再加 get() 和 join() 方法区别? 都可以阻塞线程 —— 等所有任务都执行完了再执行后续代码。 anyOf() 和 allOf() 的区别? 无返回值 推荐: 开启多线程——无返回值的——阻塞 :test06 有返回值 推荐:开启多线程——有返回值的,返回一个新的List——阻塞—

    2024年02月06日
    浏览(45)
  • 「Java」《深入解析Java多线程编程利器:CompletableFuture》

    多线程编程是指在一个程序中同时执行多个线程来提高系统的并发性和响应性。在现代计算机系统中,多线程编程已经成为开发者日常工作的一部分。以下是对多线程编程需求和挑战的介绍: 需求: 提高系统的性能:通过同时执行多个线程,可以利用多核处理器的优势,实

    2024年02月11日
    浏览(49)
  • CompletableFuture与线程池:Java 8中的高效异步编程搭配

    摘要:在Java 8中,CompletableFuture和线程池的结合使用为程序员提供了一种高效、灵活的异步编程解决方案。本文将深入探讨CompletableFuture和线程池结合使用的优势、原理及实际应用案例,帮助读者更好地理解并掌握这一技术。 随着多核处理器的普及,应用程序的性能和响应能

    2024年02月07日
    浏览(62)
  • 【CompletableFuture任务编排】游戏服务器线程模型及其线程之间的交互(以排行榜线程和玩家线程的交互为例子)

    需求: 1.我们希望玩家的业务在玩家线程执行,无需回调,因此是多线程处理。 2.匹配线程负责匹配逻辑,是单独一个线程。 3.排行榜线程负责玩家的上榜等。 4.从排行榜线程获取到排行榜列表后,需要给玩家发奖修改玩家数据,因此涉及到排行榜线程和玩家线程的交互。

    2024年01月22日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包