异步回调
引入
首先让我们来了解一下常见的调用方式,常分为三类:同步调用、回调和异步调用
| 调用方式 | 描述 |
|---|---|
| 同步调用 | 同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用 |
| 回调 | 回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口 |
| 异步调用 | 异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口) |
注意:回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。
概念
CompletableFuture在Java里面被用于异步编程,异步通常意味着非阻塞,可以使得我们的任务单独运行在与主线程分离的其他线程中,并且通过回调可以在主线程中得到异步任务的执行状态,是否完成,和是否异常等信息
与Future的区别
那么,为什么不使用Future呢?
主要原因如下:
- 不支持手动完成:这个意思指的是,我提交了一个任务,但是执行太慢了,我通过其他路径已经获取到了任务结果,现在没法把这个任务结果,通知到正在执行的线程,所以必须主动取消或者一直等待它执行完成。
- 不支持进一步的非阻塞调用:这个指的是我们通过Future的get方法会一直阻塞到任务完成,但是我还想在获取任务之后,执行额外的任务,因为Future不支持回调函数,所以无法实现这个功能。
- 不支持链式调用:这个指的是对于Future的执行结果,我们想继续传到下一个Future处理使用,从而形成一个链式的pipline调用,这在Future中是没法实现的。
- 不支持多个Future合并:比如我们有10个Future并行执行,我们想在所有的Future运行完毕之后,执行某些函数,是没法通过Future实现的。
- 不支持异常处理:Future的API没有任何的异常处理的api,所以在异步运行时,如果出了问题是不好定位的。
使用
// 异步任务,无返回值,采用内部的forkjoin线程池
CompletableFuture c1 = CompletableFuture
.runAsync(()->{System.out.println("程序运行,就不用管了")});
// 异步任务,无返回值,使用自定义的线程池
CompletableFuture c11 = CompletableFuture
.runAsync(()->{System.out.println("程序运行,就不用了")},newSingleThreadExecutor());
// 异步任务,有返回值,使用内部默认的线程池
CompletableFuture<Integer> c2 = CompletableFuture
.supplyAsync(()->{System.out.println("程序运行");return 1024;});
// 只要有一个完成,则完成,有一个抛出异常,则携带异常
CompletableFuture.anyOf(c1,c2);
// 必须等待所有的future全部完成才可以
CompletableFuture.allOf(c1,c2);