Java8 CompletableFuture(2)回调函数 thenApply thenAccept thenRun

一、前言

在上一篇介绍了CompletableFuture的创建新线程,本篇博客讲解下串行的回调函数。

二、串行的回调函数

当子线程完成后,需要调用一些回调方法,如果是Java8以前,我们写的会稍微复杂。

Java8的CompletableFuture已经为我们实现了几个回调函数,使用非常方便。

1. thenApply 转换结果

apply有“申请”、“应用”的意思,我个人理解为把上一个线程的结果“应用于”下一个线程的计算。相当于结果值的传递。

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

其中thenApply是同步的,thenApplyAsync是异步的。

Function<? super T,? extends U>

T:上一个任务返回结果的类型
U:当前任务的返回值类型

需求:

  • 在main线程里创建一个线程异步获取id=1的部门
  • 将上面线程的返回值传递给下一个任务:给user赋值部门信息,并保存user
  • 在main线程获取保存后user的值
public class Thread03_SupplyAsync_ThenApply {
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        DeptService deptService = new DeptService();
        UserService userService = new UserService();

        User user = new User(1, "冬哥", 31);

        CompletableFuture<User> userCompletableFuture = CompletableFuture.supplyAsync(() -> {

            Dept dept = deptService.getById(1);
            return dept;
        })
                .thenApplyAsync(dept -> {

                    //注意这里用到了上个线程的返回值dept
                    user.setDeptId(dept.getId());
                    user.setDeptName(dept.getName());

                    return userService.save(user);
                });


        System.out.println("线程:" + Thread.currentThread().getName() +
                " 结果:" + userCompletableFuture.get().toString());
    }
}

运行结果如下:

线程:ForkJoinPool.commonPool-worker-1 getById(1)
线程:ForkJoinPool.commonPool-worker-1 save(),User{id=1, name='冬哥', age=31, DeptId=1, DeptName='研发一部'}
线程:main 结果:User{id=1, name='冬哥', age=31, DeptId=1, DeptName='研发一部'}

如果将thenApplyAsync()替换成thenApply(),第二个任务将在主线程中同步执行,结果如下:

线程:ForkJoinPool.commonPool-worker-1 getById(1)
线程:main save(),User{id=1, name='冬哥', age=31, DeptId=1, DeptName='研发一部'}
线程:main 结果:User{id=1, name='冬哥', age=31, DeptId=1, DeptName='研发一部'}

2. thenAccept 消费结果

thenAccept 同 thenApply 接收上一个任务的返回值作为参数,但是回调方法无返回值

public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);

需求:

  • 在main线程里创建一个线程异步获取id=1的部门
  • 将上面线程的返回值dept传递给下一个任务:dept作为日志记录发给Kafka
public class Thread04_SupplyAsync_ThenAccept {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        DeptService deptService = new DeptService();

        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {

            Dept dept = deptService.getById(1);
            return dept;
        })
                .thenAcceptAsync(dept -> {

                    //注意这里用到了上个线程的返回值dept
                    System.out.println("线程:" + Thread.currentThread().getName() +
                            "假设把dept作为日志记录发给Kafka: " + dept.toString());
                    //thenAccept是没有返回值的
                });

        System.out.println("线程:" + Thread.currentThread().getName() +
                " 结果:" + voidCompletableFuture.get());
    }
}

运行结果如下:

线程:ForkJoinPool.commonPool-worker-1 getById(1)
线程:ForkJoinPool.commonPool-worker-1把dept作为日志记录发给Kafka: Dept{id=1, name='研发一部'}
线程:main 结果:null

如果thenAcceptAsync(异步)改成thenAccept(同步),结果如下:

线程:ForkJoinPool.commonPool-worker-1 getById(1)
线程:main把dept作为日志记录发给Kafka: Dept{id=1, name='研发一部'}
线程:main 结果:null

3. thenRun 任务完成后触发的回调

thenRun 是上一个任务完成后触发的回调,没有入参,也没有返回值。

public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);

需求:

  • 在main线程里创建一个线程异步获取id=1的部门
  • 上面线程结束后,执行thenRun里的回调,没有入参和返回值
public class Thread05_SupplyAsync_ThenRun {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        DeptService deptService = new DeptService();

        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {

            Dept dept = deptService.getById(1);
            return dept;
        })
                .thenRun(() -> {//注意没有入参

                    System.out.println("线程:" + Thread.currentThread().getName() + " do something");
                    //thenRun注意没有入参,也没有返回值
                });

        System.out.println("线程:" + Thread.currentThread().getName() +
                " 结果:" + voidCompletableFuture.get());
    }
}

运行结果如下:

线程:ForkJoinPool.commonPool-worker-1 getById(1)
线程:main do something
线程:main 结果:null

4. thenApply,thenAccept,thenRun,thenCompose的区别

thenApply,thenAccept,thenRun的区别如下:

特点thenApplythenAcceptthenRun
入参
返回值
`CompletableFuture` 是Java 8引入的一个高级工具类,用于处理异步计算和并发操作。其中,`supplyAsync` 和 `thenApply` 是两个关键的方法,它们一起在处理异步任务中有重要作用。 **supplyAsync方法:** `supplyAsync(Callable<T> task, Executor executor)` 方法用于异步执行一个任务。它接受一个 `Callable` 对象作为参数,这个对象会在给定的线程池(如果提供了)或默认线程池中被调用,返回值会被封装到一个新的 `CompletableFuture` 中。这使得代码可以在不阻塞主线程的情况下开始计算,提高了应用程序的响应性和并发性能。 举个例子: ```java CompletableFuture.supplyAsync(() -> { // 这里是一个耗时的操作 int result = doExpensiveComputation(); return result; }, Executors.newSingleThreadExecutor()) .thenAccept(result -> { // 处理结果 System.out.println("Result is " + result); }); ``` 在这个例子中,`doExpensiveComputation()` 是一个耗时的操作,它不会阻塞主线程。 **thenApply方法:** `thenApply(Function<T, U> after)`, 当前的 `CompletableFuture` 完成后,会应用提供的转换函数 (`after`) 到其结果上,并返回一个新的 `CompletableFuture`,新 `CompletableFuture` 的完成依赖于原 `CompletableFuture` 的完成。这样可以对原始的结果进行进一步处理,通常用于获取更复杂的数据结构或执行额外操作。 例如: ```java CompletableFuture.supplyAsync(() -> "Hello") .thenApply(str -> str.toUpperCase()) .thenAccept(System.out::println); // 输出 "HELLO" ``` 这里,我们先获取一个字符串 "Hello",然后将其转换为大写并打印。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瑟 王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值