Live2D

CompletableFuture使用

1. 介绍

CompletableFuture是Java 8中引入的一个类,用于支持异步编程和处理异步任务的结果。它提供了一种简单且强大的方式来处理异步操作,使得编写异步代码更加优雅和灵活。

以下是CompletableFuture的一些关键特性和用法介绍:

  1. 异步操作:CompletableFuture允许您执行异步操作,即在后台线程中执行任务而不会阻塞主线程。您可以使用CompletableFuture.runAsync()CompletableFuture.supplyAsync()方法来启动异步任务。
  2. 链式操作:CompletableFuture支持链式操作,您可以使用一系列的方法来组合和转换异步任务的结果。这些方法包括thenApply()thenAccept()thenCompose()thenCombine()等,它们允许您以流畅的方式处理异步任务的结果。
  3. 异常处理:CompletableFuture提供了方法来处理异步任务中的异常情况,例如exceptionally()handle()whenComplete()等。这些方法使得在异步任务出现异常时能够进行相应的处理和错误恢复。
  4. 组合多个任务:CompletableFuture提供了多种方法来组合多个异步任务的结果,包括allOf()anyOf()thenCombine()等。这些方法允许您在多个任务完成后执行进一步的操作,或者从多个任务中选择一个最先完成的结果。
  5. 超时和取消:CompletableFuture提供了超时和取消异步任务的机制。您可以使用completeOnTimeout()方法设置任务在超时后自动完成,或者使用cancel()方法取消任务的执行。
  6. 并发控制:CompletableFuture支持对异步任务进行并发控制,例如使用thenApplyAsync()方法指定任务在特定的Executor上执行,或者使用thenCompose()方法串行执行多个任务。
  7. CompletableFuture 与回调方法:CompletableFuture还支持通过回调方法来处理异步任务的结果。您可以使用thenApply()thenAccept()thenRun()等方法指定回调函数,以便在任务完成时执行相应的操作。

通过这些特性和方法,CompletableFuture为异步编程提供了强大的工具和灵活性。它可以用于处理并发、异步IO、任务调度等各种场景,使得编写高效和可维护的异步代码变得更加容易。

2.使用

以下逐个介绍CompletableFuture类中方法

supplyAsync

如果你希望获取异步线程之后的结果,可以使用supplyAsync方法

 // 将20个数扔进List集合 并返回
 CompletableFuture<ArrayList<Integer>> future = CompletableFuture.supplyAsync(() -> {
     ArrayList<Integer> list = new ArrayList<>();
     for (int i = 0; i < 20; i++) {
         list.add(i);
     }
     return list;
 });
 // 使用get方法获取异步线程结果 此方法会阻塞主线程拿到结果
 System.out.println(future.get().toString());
 System.out.println("我是主线程");

运行结果

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
我是主线程
runAsync

如果你想异步运行一些后台任务并且不想从任务中返回任何东西,可以使用runAsync方法

// 执行一个没有返回值的异步线程
// 例如读取文件信息
CompletableFuture.runAsync(()->{
    //
    try {
        BufferedReader reader = new BufferedReader(new FileReader("src/main/resources/data.txt"));
        // 演示就不流式读取
        reader.lines().forEach(System.out::println);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
});
System.out.println("我是主线程");
Thread.sleep(1000L);

运行结果

我是主线程
aa
bb
cc
dd
ee
thenApply

如果你想拿到上一个异步线程返回的结构进行后续处理并返回结果则可以使用此方法

 // supplyAsync 提供一个有返回值的异步操作
 // thenApply 可以获取上一个异步线程的结果 并返回结果
 // get 方法获取执行结果
 CompletableFuture<List<Integer>> result = CompletableFuture.supplyAsync(() -> {
     ArrayList<Integer> list = new ArrayList<>();
     for (int i = 0; i < 20; i++) {
         list.add(i);
     }
     return list;
 }).thenApply(
         res ->
                 // 基于结果集合过滤自己需要的
                 res.stream().filter(i -> i > 10).collect(Collectors.toList())
 );
 System.out.println(result.get().toString());
 System.out.println("我是主线程");
thenAccept

如果你想拿到上一个异步线程返回的结构进行后续处理不返回任何结果则可以使用此方法

// supplyAsync 提供一个有返回值的异步操作
// thenAccept 可以获取上一个异步线程的结果 不返回结果
CompletableFuture.supplyAsync(() -> {
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 20; i++) {
        list.add(i);
    }
    return list;
}).thenAccept(System.out::println);
System.out.println("我是主线程");

运行结果

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
我是主线程
complete

complete()其实也是个消费操作,但是与thenRun()不同的是,里面可以可抛出的异常

 // supplyAsync 提供一个有返回值的异步操作
 // 当执行错误 接收参数为空  异常方法exceptionally被执行
 CompletableFuture.supplyAsync(() -> {
     int a = 10 / 0;
     return "hello";
 }).whenComplete((t, action) -> System.out.println(t + "执行完成"))
         .exceptionally(t -> {
             System.out.println("执行失败:" + t.getMessage());
             return "异常";
         });
allof-多个 CompletableFuture 组合在一起
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
    // 模拟业务时长
    ThreadUtil.sleep(2000);
    System.out.println("future1当前线程:" + Thread.currentThread().getId());
    return "用户信息";
});
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("future2当前线程:" + Thread.currentThread().getId());
    return "商品信息";
});
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> {
    System.out.println("future3当前线程:" + Thread.currentThread().getId());
    return "会员信息";
});
CompletableFuture.allOf(f1,f2,f3);
System.out.println(f1.join());
System.out.println(f2.join());
System.out.println(f3.join());

3.实际应用场景

设计一个场景,前端请求洞察维度分析后立马返回任务id,后台计算。

创建任务结果表
CREATE TABLE `task_result` (
  `task_id` bigint NOT NULL COMMENT '任务id',
  `dimension` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '维度',
  `result` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '结果',
  `create_time` varchar(255) DEFAULT NULL COMMENT '时间',
  PRIMARY KEY (`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
创建用户表
CREATE TABLE `user_info` (
  `user_id` int NOT NULL COMMENT '用户id',
  `age` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '年龄  age01:0-17  age02:18-20  age03:21-40 ',
  `sex` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '性别 sex0:男  sex1:女',
  `city` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '城市  city01:北京 city02:上海 city03:深圳',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
实体类
@Data
@TableName("user_info")
@Builder
public class UserInfo {
    private Integer userId;
    private String age;
    private String sex;
    private String city;
}
@Data
@TableName("task_result")
public class TaskResult {
    private Integer taskId;
    private String dimension;
    private String result;
    private String createTime;
}

mapper层
@Repository
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
service层
public interface UserInfoService extends IService<UserInfo> {
}
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
}
插入1000万测试数据
package com.wl;

import com.wl.pojo.UserInfo;
import com.wl.service.UserInfoService;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

@SpringBootTest
@RunWith(SpringRunner.class)
public class Test {

    @Autowired
    private UserInfoService userInfoService;

    @org.junit.Test
    public void test(){
        List<UserInfo> data = data();
        System.out.println(data.size());
         userInfoService.saveBatch(data,1000);

    }


    private List<UserInfo> data() {
        List<UserInfo> list = new ArrayList<>();
        List<String> ageList = Arrays.asList("age01", "age02", "age02");
        List<String> sexList = Arrays.asList("sex0", "sex1");
        List<String> cityList = Arrays.asList("city01", "city02","city03");

        for (int i = 0; i < 10000000; i++) {
            UserInfo userInfo = UserInfo.builder()
                    .userId(i)
                    .age(ageList.get(new Random().nextInt(3)))
                    .sex(sexList.get(new Random().nextInt(2)))
                    .city(cityList.get(new Random().nextInt(3)))
                    .build();
            list.add(userInfo);
        }
        return list;
    }
}

查看数据库

image

service业务代码
package com.wl.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wl.mapper.TaskResultMapper;
import com.wl.mapper.UserInfoMapper;
import com.wl.pojo.CountResult;
import com.wl.pojo.TaskResult;
import com.wl.pojo.UserInfo;
import com.wl.utils.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;


/**
 * @Author NoDreamJava
 * @Date 2023-6-16 11:20
 * @Version 1.0
 */
@Service
@DS("test")
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private TaskResultMapper taskResultMapper;

    @Override
    public Long countInfoByDimension(Map<String, String> param) {

        long taskId = System.currentTimeMillis();

        String dimensionList = param.get("dimension");

        for (String dimension : dimensionList.split(",")) {
            CompletableFuture.runAsync(()->whenCompletable(taskId,dimension));
        }


        return taskId;

    }

    private void test01(String dimension) {
        CompletableFuture.runAsync(()->{

            CompletableFuture<String> future=CompletableFuture.supplyAsync(() ->
                    {
                        List<CountResult> result = userInfoMapper.countInfoByDimension(dimension);
                        List<Map<Object, Object>> collect = result.stream().map(c -> MapUtils.mapOf(c.getColumnName(), c.getCountNum())).collect(Collectors.toList());
                        return JSON.toJSONString(collect);
                    }
            );

            try {
                test(future);
            } catch (Exception e) {
                e.printStackTrace();
            }

        });
    }

    private void test(CompletableFuture<String> future) throws InterruptedException, java.util.concurrent.ExecutionException {
        while (true) {
            if (future.isDone() && !future.isCancelled()) {
                String s = future.get();
                if (s == null) {
                    break;
                }
                TaskResult taskResult=TaskResult.builder()
                        .taskId(System.currentTimeMillis())
                        .result(s).build();
                taskResultMapper.insert(taskResult);
                break;
            }
        }
    }

    private void whenCompletable(long taskId, String dimension) {
        // 分维度查询
        CompletableFuture.supplyAsync(() -> userInfoMapper.countInfoByDimension(dimension)
        ).whenComplete(
                (result,action) -> {

                    List<Map<Object, Object>> collect = result.stream().map(c -> MapUtils.mapOf(c.getColumnName(), c.getCountNum())).collect(Collectors.toList());

                    String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                    // 拼接参数
                    TaskResult taskResult=TaskResult.builder().taskId(taskId).dimension(dimension)
                            .createTime(format)
                            .result(JSON.toJSONString(collect)).build();
                    System.out.println(taskResult);

                    taskResultMapper.insert(taskResult);
                }
        ).join();
    }
}

posted @ 2023-06-16 16:46  没有梦想的java菜鸟  阅读(233)  评论(0编辑  收藏  举报